Source code for molify.compress
import ase
import numpy as np
from ase.cell import Cell
from molify.utils import calculate_box_dimensions
[docs]
def compress(
atoms: ase.Atoms,
density: float,
freeze_molecules: bool = False,
) -> ase.Atoms:
"""Compress an ASE Atoms object to a target density.
Arguments
---------
atoms : ase.Atoms
The Atoms object to compress.
density : float
The target density in g/cm^3.
freeze_molecules : bool
If True, freeze the internal degrees of freedom of the molecules
during compression, to prevent bond compression.
"""
atoms = atoms.copy()
new_dimensions = np.array(calculate_box_dimensions([atoms], density))
if freeze_molecules:
new_cell = Cell.new(new_dimensions)
old_cell = atoms.get_cell()
connectivity = atoms.info.get("connectivity")
if connectivity is None:
raise ValueError("No connectivity info found for freeze_molecules=True")
# Build molecular fragments
from collections import defaultdict
groups = defaultdict(set)
parent = {}
def find(x):
while parent.get(x, x) != x:
x = parent[x]
return x
def union(x, y):
rx, ry = find(x), find(y)
if rx != ry:
parent[ry] = rx
for i, j, _ in connectivity:
union(i, j)
for idx in range(len(atoms)):
root = find(idx)
groups[root].add(idx)
t_mat = np.linalg.solve(old_cell.T, new_cell.T)
positions = atoms.get_positions()
for group in groups.values():
group = list(group)
com = atoms[group].get_center_of_mass()
com_new = com @ t_mat
shift = com_new - com
positions[group] += shift
atoms.set_positions(positions)
atoms.set_cell(new_cell, scale_atoms=False)
else:
atoms.set_cell(new_dimensions, scale_atoms=True)
return atoms