Source code for molsystem.align
# -*- coding: utf-8 -*-
"""Alignment methods for configurations"""
import logging
try:
import openbabel # noqa: F401
import openbabel.openbabel as ob
except ModuleNotFoundError:
print(
"Please install openbabel using conda:\n"
" conda install -c conda-forge openbabel"
)
raise
logger = logging.getLogger(__name__)
[docs]
class AlignMixin:
"""A mixin for handling alignment of configuration."""
[docs]
def RMSD(self, other, include_h=True, symmetry=False):
"""Compute the RMSD between configurations.
Parameters
----------
other : Configuration or iterable of Configurations
A single configuration or e.g. list of configurations to match
include_h : bool = True
Whether to include hydrogen atoms in the RMSD
symmetry : bool = False
Whether to detect symmetric flips. Note this requires a lot of memory for
larger systems.
Returns
-------
float or [float]
The RMS between the current configuration and the target(s).
"""
from .configuration import _Configuration
align = ob.OBAlign(include_h, symmetry)
align.SetRefMol(self.to_OBMol())
if isinstance(other, _Configuration):
align.SetTargetMol(other.to_OBMol())
align.Align()
return align.GetRMSD()
elif isinstance(other, ob.OBMol):
align.SetTargetMol(other)
align.Align()
return align.GetRMSD()
result = []
try:
for target in other:
if isinstance(target, _Configuration):
align.SetTargetMol(target.to_OBMol())
align.Align()
result.append(align.GetRMSD())
elif isinstance(target, ob.OBMol):
align.SetTargetMol(target)
align.Align()
result.append(align.GetRMSD())
else:
raise TypeError(f"RMSD can't handle {type(target)}.")
except TypeError:
raise
except Exception as e:
logger.error(f"Configuration.RMSD {e}")
raise
return result
[docs]
def align(self, other, include_h=True, symmetry=False):
"""Align configurations.
Parameters
----------
other : Configuration or iterable of Configurations
A single configuration or e.g. list of configurations to match
include_h : bool = True
Whether to include hydrogen atoms in the RMSD
symmetry : bool = False
Whether to detect symmetric flips. Note this requires a lot of memory for
larger systems.
Returns
-------
float or [float]
The RMS between the current configuration and the target(s).
"""
from .configuration import _Configuration
align = ob.OBAlign(include_h, symmetry)
align.SetRefMol(self.to_OBMol())
if isinstance(other, _Configuration):
mol = other.to_OBMol()
align.SetTargetMol(mol)
align.Align()
align.UpdateCoords(mol)
other.coordinates_from_OBMol(mol)
return align.GetRMSD()
elif isinstance(other, ob.OBMol):
align.SetTargetMol(other)
align.Align()
align.UpdateCoords(other)
return align.GetRMSD()
result = []
try:
for target in other:
if isinstance(target, _Configuration):
mol = target.to_OBMol()
align.SetTargetMol(mol)
align.Align()
align.UpdateCoords(mol)
target.coordinates_from_OBMol(mol)
result.append(align.GetRMSD())
elif isinstance(target, ob.OBMol):
align.SetTargetMol(target)
align.Align()
align.UpdateCoords(target)
result.append(align.GetRMSD())
else:
raise TypeError(f"RMSD can't handle {type(target)}.")
except TypeError:
raise
except Exception as e:
logger.error(f"Configuration.RMSD {e}")
raise
return result