Source code for bonafide.features.multiwfn_bond_misc
"""Miscellaneous bond features from ``Multiwfn``."""
import os
from typing import List, Union
from bonafide.utils.base_featurizer import BaseFeaturizer
from bonafide.utils.constants import PROGRAM_ENVIRONMENT_VARIABLES
from bonafide.utils.driver import multiwfn_driver
from bonafide.utils.io_ import write_xyz_file_from_coordinates_array
[docs]
class _Multiwfn3DBond(BaseFeaturizer):
"""Parent feature factory for the 3D bond Multiwfn features.
For details, please refer to the Multiwfn manual (http://sobereva.com/multiwfn/, last accessed
on 12.09.2025).
"""
def __init__(self) -> None:
self.extraction_mode = "single"
super().__init__()
[docs]
def calculate(self) -> None:
"""Calculate the Multiwfn bond features."""
# For the data calculated here, no electronic structure data is required. Therefore, it is
# calculated from the XYZ file.
# Generate the XYZ file and write it to the electronic_struc_n attribute for input
# to Multiwfn
self._write_input_file()
self._run_multiwfn()
self._read_output_file()
[docs]
def _run_multiwfn(self) -> None:
"""Run Multiwfn.
Returns
-------
None
"""
# Select other functions
multiwfn_commands: List[Union[str, int, float]]
multiwfn_commands = [100]
# Select structure info data
multiwfn_commands.append(21)
# Define bond
bond = self.mol.GetBondWithIdx(self.atom_bond_idx)
begin_atom_idx = bond.GetBeginAtomIdx()
end_atom_idx = bond.GetEndAtomIdx()
multiwfn_commands.append(f"{begin_atom_idx + 1},{end_atom_idx + 1}")
# Exit program
multiwfn_commands.extend(["q", 0, "q"])
# Set up environment variables
environment_variables = {
var: getattr(self, var, None) for var in PROGRAM_ENVIRONMENT_VARIABLES["multiwfn"]
}
# Run Multiwfn
multiwfn_driver(
cmds=multiwfn_commands,
input_file_path=self.conformer_name + ".xyz",
output_file_name=f"Multiwfn3DBondMiscInfo__{self.conformer_name}__"
f"bond-{self.atom_bond_idx}",
environment_variables=environment_variables,
namespace=self.conformer_name[::-1].split("__", 1)[-1][::-1],
)
[docs]
def _read_output_file(self) -> None:
"""Read the output file from Multiwfn and write the results to the ``results`` dictionary.
Returns
-------
None
"""
# Check if the output file exists
_opath = f"Multiwfn3DBondMiscInfo__{self.conformer_name}__bond-{self.atom_bond_idx}.out"
if os.path.isfile(_opath) is False:
self._err = (
f"Multiwfn output file '{_opath}' not found; probably the calculation "
"did not run. Check your input"
)
return
# Open output file
with open(_opath, "r") as f:
multiwfn_output = f.readlines()
# Find the values and write them to the results dictionary
for line_idx, line in enumerate(multiwfn_output):
if line.startswith(" Center of mass (X/Y/Z):"):
splitted = line.split()
x = splitted[-4].strip()
y = splitted[-3].strip()
z = splitted[-2].strip()
self.results[self.atom_bond_idx] = {
"multiwfn3D-bond-coordinates_center_of_mass": ",".join([x, y, z]),
}
if line.startswith(" Center of nuclear charges (X/Y/Z):"):
splitted = line.split()
x = splitted[-4].strip()
y = splitted[-3].strip()
z = splitted[-2].strip()
self.results[self.atom_bond_idx][
"multiwfn3D-bond-coordinates_center_of_nuclear_charges"
] = ",".join([x, y, z])
if line.startswith(" Electrostatic interaction energy between nuclear charges:"):
splitted = multiwfn_output[line_idx + 1].split()
self.results[self.atom_bond_idx][
"multiwfn3D-bond-electrostatic_interaction_energy_nuclear_charges"
] = float(splitted[-2])
if line.startswith(" Radius of gyration:"):
val = float(line.split(":")[-1].split()[0])
self.results[self.atom_bond_idx]["multiwfn3D-bond-gyration_radius"] = val
[docs]
class Multiwfn3DBondCoordinatesCenterOfMass(_Multiwfn3DBond):
"""Feature factory for the 3D bond feature "coordinates_center_of_mass", calculated with
multiwfn.
The index of this feature is 432 (see the ``list_atom_features()`` and
``list_bond_features()`` method). The corresponding configuration settings can be found
under "multiwfn.misc" in the _feature_config.toml file.
"""
def __init__(self) -> None:
super().__init__()
# This feature is automatically calculated in _Multiwfn3DBond
[docs]
class Multiwfn3DBondCoordinatesCenterOfNuclearCharges(_Multiwfn3DBond):
"""Feature factory for the 3D bond feature "coordinates_center_of_nuclear_charges",
calculated with multiwfn.
The index of this feature is 433 (see the ``list_atom_features()`` and
``list_bond_features()`` method). The corresponding configuration settings can be found
under "multiwfn.misc" in the _feature_config.toml file.
"""
def __init__(self) -> None:
super().__init__()
# This feature is automatically calculated in _Multiwfn3DBond
[docs]
class Multiwfn3DBondElectrostaticInteractionEnergyNuclearCharges(_Multiwfn3DBond):
"""Feature factory for the 3D bond feature
"electrostatic_interaction_energy_nuclear_charges", calculated with multiwfn.
The index of this feature is 434 (see the ``list_atom_features()`` and
``list_bond_features()`` method). The corresponding configuration settings can be found
under "multiwfn.misc" in the _feature_config.toml file.
"""
def __init__(self) -> None:
super().__init__()
# This feature is automatically calculated in _Multiwfn3DBond
[docs]
class Multiwfn3DBondGyrationRadius(_Multiwfn3DBond):
"""Feature factory for the 3D bond feature "gyration_radius", calculated with multiwfn.
The index of this feature is 439 (see the ``list_atom_features()`` and
``list_bond_features()`` method). The corresponding configuration settings can be found
under "multiwfn.misc" in the _feature_config.toml file.
"""
def __init__(self) -> None:
super().__init__()
# This feature is automatically calculated in _Multiwfn3DBond