Skip to content

utils

ANONYMOUS_ELEMENTS = tuple(itertools.islice(anonymous_element_generator(), 150)) module-attribute

Returns the first 150 values of the anonymous element generator.

ATOMIC_NUMBERS = {} module-attribute

CHEMICAL_FORMULA_REGEXP = '(^$)|^([A-Z][a-z]?([2-9]|[1-9]\\d+)?)+$' module-attribute

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

ELEMENT_SYMBOLS_PATTERN = '(' + '|'.join(CHEMICAL_SYMBOLS) + ')' module-attribute

EXTENDED_CHEMICAL_SYMBOLS_PATTERN = '(' + '|'.join(CHEMICAL_SYMBOLS + EXTRA_SYMBOLS) + ')' module-attribute

EXTRA_SYMBOLS = ['X', 'vacancy'] module-attribute

IDENTIFIER_REGEX = '^[a-z_][a-z_0-9]+$' module-attribute

OPTIMADE_SCHEMA_EXTENSION_KEYS = ['support', 'queryable', 'unit', 'sortable'] module-attribute

OPTIMADE_SCHEMA_EXTENSION_PREFIX = 'x-optimade-' module-attribute

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

SupportLevel

Bases: Enum

OPTIMADE property/field support levels

Source code in optimade/models/utils.py
32
33
34
35
36
37
class SupportLevel(Enum):
    """OPTIMADE property/field support levels"""

    MUST = "must"
    SHOULD = "should"
    OPTIONAL = "optional"

MUST = 'must' class-attribute instance-attribute

OPTIONAL = 'optional' class-attribute instance-attribute

SHOULD = 'should' class-attribute instance-attribute

OptimadeField(default=PydanticUndefined, *, support=None, queryable=None, unit=None, **kwargs)

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 Default
support str | SupportLevel | None

The support level of the field itself, i.e. whether the field can be null or omitted by an implementation.

None
queryable str | SupportLevel | None

The support level corresponding to the queryablility of this field.

None
unit str | None

A string describing the unit of the field.

None

Returns:

Type Description
Any

The pydantic field with extra validation provided by StrictField.

Source code in optimade/models/utils.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def OptimadeField(
    default: "Any" = PydanticUndefined,
    *,
    support: str | SupportLevel | None = None,
    queryable: str | SupportLevel | None = None,
    unit: str | None = None,
    **kwargs,
) -> Any:
    """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.

    Arguments:
        support: The support level of the field itself, i.e. whether the field
            can be null or omitted by an implementation.
        queryable: The support level corresponding to the queryablility
            of this field.
        unit: A string describing the unit of the field.

    Returns:
        The pydantic field with extra validation provided by [`StrictField`][optimade.models.utils.StrictField].

    """

    # Collect non-null keyword arguments to add to the Field schema
    if unit is not None:
        kwargs["unit"] = unit

    if queryable is not None:
        if isinstance(queryable, str):
            queryable = SupportLevel(queryable.lower())
        kwargs["queryable"] = queryable

    if support is not None:
        if isinstance(support, str):
            support = SupportLevel(support.lower())
        kwargs["support"] = support

    return StrictField(default, **kwargs)

StrictField(default=PydanticUndefined, *, description=None, **kwargs)

A wrapper around pydantic.Field that does the following:

  • Forbids any "extra" keys that would be passed to 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".
  • Emits a warning when no description is provided.

Parameters:

Name Type Description Default
default Any

The only non-keyword argument allowed for Field.

PydanticUndefined
description str | None

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 Description
RuntimeError

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 Description
Any

The pydantic Field.

Source code in optimade/models/utils.py
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
def StrictField(
    default: "Any" = PydanticUndefined,
    *,
    description: str | None = None,
    **kwargs: "Any",
) -> Any:
    """A wrapper around `pydantic.Field` that does the following:

    - Forbids any "extra" keys that would be passed to `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".
    - Emits a warning when no description is provided.

    Arguments:
        default: The only non-keyword argument allowed for Field.
        description: The description of the `Field`; if this is not
            specified then a `UserWarning` will be emitted.
        **kwargs: Extra keyword arguments to be passed to `Field`.

    Raises:
        RuntimeError: 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:
        The pydantic `Field`.

    """
    allowed_schema_and_field_keys = ["pattern"]

    allowed_keys = [
        "pattern",
        "uniqueItems",
    ] + OPTIMADE_SCHEMA_EXTENSION_KEYS
    _banned = [k for k in kwargs if k not in set(_PYDANTIC_FIELD_KWARGS + allowed_keys)]

    if _banned:
        raise RuntimeError(
            f"Not creating StrictField({default!r}, **{kwargs!r}) with "
            f"forbidden keywords {_banned}."
        )

    # Handle description
    if description is None:
        warnings.warn(
            f"No description provided for StrictField specified by {default!r}, "
            f"**{kwargs!r}."
        )
    else:
        kwargs["description"] = description

    # OPTIMADE schema extensions
    json_schema_extra: dict[str, Any] = kwargs.pop("json_schema_extra", {})

    # Go through all JSON Schema keys and add them to the json_schema_extra.
    for key in allowed_keys:
        if key not in kwargs:
            continue

        # If they are OPTIMADE schema extensions, add them with the OPTIMADE prefix.
        schema_key = (
            f"{OPTIMADE_SCHEMA_EXTENSION_PREFIX}{key}"
            if key in OPTIMADE_SCHEMA_EXTENSION_KEYS
            else key
        )

        for key_variant in (key, schema_key):
            if key_variant in json_schema_extra:
                if json_schema_extra.pop(key_variant) != kwargs[key]:
                    raise RuntimeError(
                        f"Conflicting values for {key} in json_schema_extra and kwargs."
                    )

        json_schema_extra[schema_key] = (
            kwargs[key] if key in allowed_schema_and_field_keys else kwargs.pop(key)
        )

    kwargs["json_schema_extra"] = json_schema_extra

    return Field(default, **kwargs)

anonymize_formula(formula)

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 Description
str

The anonymous chemical formula in the OPTIMADE representation.

Source code in optimade/models/utils.py
209
210
211
212
213
214
215
216
217
218
def anonymize_formula(formula: str) -> str:
    """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:
        The anonymous chemical formula in the OPTIMADE representation.

    """
    return _reduce_or_anonymize_formula(formula, alphabetize=False, anonymize=True)

anonymous_element_generator()

Generator that yields the next symbol in the A, B, Aa, ... Az naming scheme.

Source code in optimade/models/utils.py
165
166
167
168
169
170
171
172
173
def anonymous_element_generator() -> "Generator[str, None, None]":
    """Generator that yields the next symbol in the A, B, Aa, ... Az naming scheme."""
    from string import ascii_lowercase

    for size in itertools.count(1):
        for tuple_strings in itertools.product(ascii_lowercase, repeat=size):
            list_strings = list(tuple_strings)
            list_strings[0] = list_strings[0].upper()
            yield "".join(list_strings)

reduce_formula(formula)

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 Description
str

The reduced chemical formula in the OPTIMADE representation.

Source code in optimade/models/utils.py
221
222
223
224
225
226
227
228
229
def reduce_formula(formula: str) -> str:
    """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:
        The reduced chemical formula in the OPTIMADE representation.

    """
    return _reduce_or_anonymize_formula(formula, alphabetize=True, anonymize=False)