Source code for bonafide.features.oxidation_state

"""Oxidation state feature."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Dict

from rdkit import Chem

from bonafide.utils.base_featurizer import BaseFeaturizer
from bonafide.utils.helper_functions import get_function_or_method_name
from bonafide.utils.helper_functions_chemistry import from_periodic_table

if TYPE_CHECKING:
    from mendeleev import element


[docs] class Bonafide2DAtomOxidationState(BaseFeaturizer): """Feature factory for the 2D atom feature "oxidation_state", implemented within this package. The index of this feature is 36 (see the ``list_atom_features()`` and ``list_bond_features()`` method). The corresponding configuration settings can be found under "bonafide.oxidation_state" in the _feature_config.toml file. """ _periodic_table: Dict[str, element] en_scale: str def __init__(self) -> None: self.extraction_mode = "single" super().__init__()
[docs] def calculate(self) -> None: """Calculate the ``bonafide2D-atom-oxidation_state`` feature.""" _loc = f"{self.__class__.__name__}.{get_function_or_method_name()}" # Get electronegativity for the atom under consideration atom = self.mol.GetAtomWithIdx(self.atom_bond_idx) _, element_data = from_periodic_table( periodic_table=self._periodic_table, element_symbol=atom.GetSymbol() ) atom_en = element_data.electronegativity(scale=self.en_scale) # Get the data from the neighbors contributions = [] for neighbor in atom.GetNeighbors(): _, element_data_neighbor = from_periodic_table( periodic_table=self._periodic_table, element_symbol=neighbor.GetSymbol() ) neighbor_en = element_data_neighbor.electronegativity(scale=self.en_scale) neighbor_bond = self.mol.GetBondBetweenAtoms(atom.GetIdx(), neighbor.GetIdx()) neighbor_bond_order = neighbor_bond.GetBondTypeAsDouble() if neighbor_en > atom_en: contributions.append(neighbor_bond_order) elif neighbor_en < atom_en: contributions.append(-neighbor_bond_order) # Calculate the oxidation state and save the results value = int(sum(contributions) + atom.GetFormalCharge()) self.results[self.atom_bond_idx] = {self.feature_name: value} # Log a warning if oxidation states are calculated without hydrogen atoms _helper_mol = Chem.Mol(self.mol) _helper_mol = Chem.AddHs(_helper_mol) if _helper_mol.GetNumAtoms() != self.mol.GetNumAtoms(): _namespace = self.conformer_name[::-1].split("__", 1)[-1][::-1] logging.warning( f"'{_namespace}' | {_loc}()\nThe '{self.feature_name}' feature was calculated " f"for atom with index '{self.atom_bond_idx}' without adding hydrogen atoms to the " "molecule. Check if this is desired." )