File: bonds.py

package info (click to toggle)
python-emmet-core 0.84.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 77,220 kB
  • sloc: python: 16,355; makefile: 30
file content (109 lines) | stat: -rw-r--r-- 3,845 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import logging
from typing import Dict, List, Any

import numpy as np
from pydantic import Field
from pymatgen.analysis.graphs import StructureGraph
from pymatgen.analysis.local_env import NearNeighbors

from emmet.core.material_property import PropertyDoc

AVAILABLE_METHODS = {nn.__name__: nn for nn in NearNeighbors.__subclasses__()}


class BondingDoc(PropertyDoc):
    """Structure graphs representing chemical bonds calculated from structure
    using near neighbor strategies as defined in pymatgen."""

    property_name: str = "bonding"

    structure_graph: StructureGraph = Field(
        description="Structure graph",
    )

    method: str = Field(description="Method used to compute structure graph.")

    bond_types: Dict[str, List[float]] = Field(
        description="Dictionary of bond types to their length, e.g. a Fe-O to "
        "a list of the lengths of Fe-O bonds in Angstrom."
    )
    bond_length_stats: Dict[str, Any] = Field(
        description="Dictionary of statistics of bonds in structure "
        "with keys all_weights, min, max, mean and variance."
    )
    coordination_envs: List[str] = Field(
        description="List of co-ordination environments, e.g. ['Mo-S(6)', 'S-Mo(3)']."
    )
    coordination_envs_anonymous: List[str] = Field(
        description="List of co-ordination environments without elements "
        "present, e.g. ['A-B(6)', 'A-B(3)']."
    )

    @classmethod
    def from_structure(
        cls,
        structure,
        material_id,
        preferred_methods=(
            "CrystalNN",
            "MinimumDistanceNN",
        ),
        **kwargs
    ):
        """
        Calculate

        :param structure: ideally an oxidation state-decorated structure
        :param material_id: mpid
        :param preferred_methods: list of strings of NearNeighbor classes or NearNeighbor instances
        :param deprecated: whether source material is or is not deprecated
        :param kwargs: to pass to PropertyDoc
        :return:
        """

        bonding_info = None
        preferred_methods = [  # type: ignore
            AVAILABLE_METHODS[method]() if isinstance(method, str) else method
            for method in preferred_methods
        ]

        for method in preferred_methods:
            try:
                sg = StructureGraph.with_local_env_strategy(structure, method)

                # ensure edge weights are specifically bond lengths
                edge_weights = []
                for u, v, d in sg.graph.edges(data=True):
                    jimage = np.array(d["to_jimage"])
                    dist = sg.structure.get_distance(u, v, jimage=jimage)
                    edge_weights.append((u, v, d["to_jimage"], dist))
                for u, v, to_jimage, dist in edge_weights:
                    sg.alter_edge(u, v, to_jimage=to_jimage, new_weight=dist)

                bonding_info = {
                    "method": method.__class__.__name__,
                    "structure_graph": sg,
                    "bond_types": sg.types_and_weights_of_connections,
                    "bond_length_stats": sg.weight_statistics,
                    "coordination_envs": sg.types_of_coordination_environments(),
                    "coordination_envs_anonymous": sg.types_of_coordination_environments(
                        anonymous=True
                    ),
                }

                break

            except Exception as e:
                logging.warning(
                    "Failed to calculate bonding: {} {} {}".format(
                        material_id, method, e
                    )
                )

        if bonding_info:
            return super().from_structure(
                meta_structure=structure,
                material_id=material_id,
                **bonding_info,
                **kwargs
            )