Source code for bonafide.features.morfeus_sasa
"""Solvent-accessible surface features from ``MORFEUS``."""
from __future__ import annotations
import pickle
from typing import TYPE_CHECKING, List, Optional, Union
import numpy as np
from morfeus import SASA
from bonafide.utils.base_featurizer import BaseFeaturizer
if TYPE_CHECKING:
from numpy.typing import NDArray
[docs]
class _Morfeus3DAtomSASA(BaseFeaturizer):
"""Parent feature factory for the 3D atom MORFEUS SASA features.
For details, please refer to the MORFEUS documentation
(https://digital-chemistry-laboratory.github.io/morfeus/index.html, last accessed on
09.09.2025).
"""
sasa_: SASA
density: float
probe_radius: float
radii: Optional[Union[List[float], NDArray[np.float64]]]
radii_type: str
def __init__(self) -> None:
self.extraction_mode = "multi"
super().__init__()
[docs]
def _run_morfeus(self) -> None:
"""Run MORFEUS and populate the solvent-accessible surface area attribute (``sasa_``).
Returns
-------
None
"""
# Modify the user input if necessary to comply with MORFEUS requirements
if self.radii == []:
self.radii = None
else:
self.radii = np.array(self.radii)
# Run MORFEUS
assert self.coordinates is not None # for type checker
self.sasa_ = SASA(
elements=self.elements,
coordinates=self.coordinates,
radii=self.radii,
radii_type=self.radii_type,
probe_radius=self.probe_radius,
density=self.density,
)
# Save data
with open(f"{self.__class__.__name__}__{self.conformer_name}.pkl", "wb") as f:
pickle.dump(self.sasa_, f)
[docs]
class Morfeus3DAtomSasAtomArea(_Morfeus3DAtomSASA):
"""Feature factory for the 3D atom feature "sas_atom_area", calculated with morfeus.
The index of this feature is 196 (see the ``list_atom_features()`` and
``list_bond_features()`` method). The corresponding configuration settings can be found
under "morfeus.sasa" in the _feature_config.toml file.
"""
def __init__(self) -> None:
super().__init__()
[docs]
def calculate(self) -> None:
"""Calculate the ``morfeus3D-atom-sas_atom_area`` feature."""
self._run_morfeus()
for atom_idx, value in self.sasa_.atom_areas.items():
self.results[atom_idx - 1] = {self.feature_name: float(value)} # morfeus is 1-indexed
[docs]
class Morfeus3DAtomSasFractionAtomArea(_Morfeus3DAtomSASA):
"""Feature factory for the 3D atom feature "sas_fraction_atom_area", calculated with
morfeus.
The index of this feature is 197 (see the ``list_atom_features()`` and
``list_bond_features()`` method). The corresponding configuration settings can be found
under "morfeus.sasa" in the _feature_config.toml file.
"""
def __init__(self) -> None:
super().__init__()
[docs]
def calculate(self) -> None:
"""Calculate the ``morfeus3D-atom-sas_fraction_atom_area`` feature."""
self._run_morfeus()
for atom_idx, value in self.sasa_.atom_areas.items():
ratio = value / self.sasa_.area
self.results[atom_idx - 1] = {self.feature_name: float(ratio)} # morfeus is 1-indexed