def setup_combos(): atoms = setup_atoms() # Fix linear combination of two bond lengths with atom indices 0-8 and # 0-6 with weighting coefficients 1.0 and -1.0 to the current value. # In other words, fulfil the following constraint: # 1.0 * atoms.get_distance(2, 1) + -1.0 * atoms.get_distance(2, 3) = const. bondcombo_def = [[2, 1, 1.0], [2, 3, -1.0]] target_bondcombo = get_bondcombo(atoms, bondcombo_def) # Fix linear combination of two angles # 1. * atoms.get_angle(7, 0, 8) + 1. * atoms.get_angle(7, 0, 6) = const. anglecombo_def = [[7, 0, 8, 1.], [7, 0, 6, 1]] target_anglecombo = get_anglecombo(atoms, anglecombo_def) # Fix linear combination of two dihedrals dihedralcombo_def = [[3, 2, 1, 4, 1.0], [2, 1, 0, 7, 1.0]] target_dihedralcombo = get_dihedralcombo(atoms, dihedralcombo_def) # Initialize constraint constr = FixInternals(bondcombos=[(target_bondcombo, bondcombo_def)], anglecombos=[(target_anglecombo, anglecombo_def)], dihedralcombos=[(target_dihedralcombo, dihedralcombo_def)], epsilon=1e-10) print(constr) return (atoms, constr, bondcombo_def, target_bondcombo, anglecombo_def, target_anglecombo, dihedralcombo_def, target_dihedralcombo)
def setup_fixinternals(): atoms = setup_atoms() # Angles, Bonds, Dihedrals are built up with pairs of constraint # value and indices defining the constraint # Linear combinations of bond lengths are built up similarly with the # coefficients appended to the indices defining the constraint # Fix bond between atoms 1 and 2 to 1.4 bond_def = [1, 2] target_bond = 1.4 # Fix angle to whatever it was from the start angle_def = [6, 0, 1] target_angle = atoms.get_angle(*angle_def) # Fix this dihedral angle to whatever it was from the start dihedral_def = [6, 0, 1, 2] target_dihedral = atoms.get_dihedral(*dihedral_def) # Initialize constraint constr = FixInternals(bonds=[(target_bond, bond_def)], angles_deg=[(target_angle, angle_def)], dihedrals_deg=[(target_dihedral, dihedral_def)], epsilon=1e-10) print(constr) return (atoms, constr, bond_def, target_bond, angle_def, target_angle, dihedral_def, target_dihedral)
def evaluate_energy(angles: Union[List[float], np.ndarray], atoms: Atoms, dihedrals: List[DihedralInfo], calc: Calculator, relax: bool = True) -> float: """Compute the energy of a cysteine molecule given dihedral angles Args: angles: List of dihedral angles atoms: Structure to optimize dihedrals: Description of the dihedral angles calc: Calculator used to compute energy/gradients relax: Whether to relax the non-dihedral degrees of freedom Returns: - (float) energy of the structure """ # Make a copy of the input atoms = atoms.copy() # Set the dihedral angles to desired settings dih_cnsts = [] for a, di in zip(angles, dihedrals): atoms.set_dihedral(*di.chain, a, indices=di.group) # Define the constraints dih_cnsts.append((a, di.chain)) # If not relaxed, just compute the energy if not relax: return calc.get_potential_energy(atoms) atoms.set_constraint() atoms.set_constraint(FixInternals(dihedrals_deg=dih_cnsts)) return relax_structure(atoms, calc)[0]
def _constrain_molecule(atoms, rattle=0.00001): """Helper function for place_and_preoptimize_adsorbates Args: atoms (ase.Atoms) : adsorbate molecule rattle (float) : standard deviation of the structure rattling Returns: list : list of different constraints from ase.constraints bonds, angles and dihedrals other than those of sp3-centers are constrained """ # Rattling is important, otherwise the optimization fails! atoms.rattle(stdev=rattle) nb_lst, nl = _get_neighbours(atoms, buffer_factor=1.5) fb = FixBondLengths(nb_lst) fa = _fix_nearest_atom_to_x(atoms, nl) angles = _get_all_angles(atoms, nb_lst) dihedrals = _get_all_dihedrals(atoms, angles) # keep dihedrals with sp2 or sp center # use neighbor list and a custom dictionary for number of nearest neighbors dihedrals = _filter_dihedrals(atoms, nl, dihedrals) print("kept dhls", dihedrals) fi = FixInternals(angles= angles, dihedrals = dihedrals) hookean_lst = _hookean_bonds(atoms, nb_lst) # IMPORTANT! fix binding atom at the end, otherwise it moves! atoms.set_constraint(hookean_lst.extend([fi, fb, fa])) return atoms
def adjust_z_axis(self, cluster): if self.surface == None: return ''' surface_constraints = [] for atom in self.surface: surface_constraints.append(MyConstraint(atom.index,(0,0,1))) ''' surface_constraints = [] #surface_constraints.append(ExternalForce(0, 0, 10)) all_bonds = [] all_angles = [] all_dihedrals = [] for index_1 in range(len(self.surface)): for index_2 in range(index_1+1,len(self.surface)): bonds_distance = get_distance(self.surface[index_1],self.surface[index_2]) bond = [bonds_distance, [index_1,index_2]] all_bonds.append(bond) """ for index_3 in range(index_2+1,len(self.surface)): angle_indices = [[index_1,index_2,index_3],[index_3,index_1,index_2],[index_2,index_3,index_1]] for angle_indice in angle_indices: angle = [self.surface.get_angle(*angle_indice) * pi / 180, angle_indice] all_angles.append(angle) ''' for index_4 in range(index_3+1,len(self.surface)): dihedral_indices = [[index_1,index_2,index_3,index_4],[index_1,index_2,index_3,index_4],[index_1,index_2,index_3,index_4]] for dihedral_indice in dihedral_indices: dihedral = [self.surface.get_dihedral(*dihedral_indice) * pi / 180, dihedral_indice] all_dihedrals.append(dihedral) ''' """ surface_constraints.append(FixInternals(bonds=all_bonds, angles=all_angles, dihedrals=all_dihedrals)) #self.surface.set_constraint(surface_constraints) c = FixAtoms(indices=range(len(self.surface),len(self.surface)+len(cluster))) cluster_constraints = [c] #cluster.set_constraint(cluster_constraints) for atom in self.surface: atom.z -= 40.0 system = self.surface + cluster system.set_calculator(EMT()) system.set_constraint(surface_constraints+cluster_constraints) dyn = FIRE(system) converged = False import pdb; pdb.set_trace() try: dyn.run(fmax=0.01,steps=5000) converged = dyn.converged() if not converged: import os name = os.path.basename(os.getcwd()) errorMessage = 'The optimisation of cluster ' + name + ' did not optimise completely.' print(errorMessage, file=sys.stderr) print(errorMessage) except: print('Local Optimiser Failed for some reason.') import pdb; pdb.set_trace()
def __init__(self, repeat): nmol = repeat[0] * repeat[1] * repeat[2] r = rOH a = angleHOH * pi / 180 # From https://doi.org/10.1063/1.445869 eexp = 6.24 * units.kcal / units.mol dexp = 2.75 aexp = 46 D = np.linspace(2.5, 3.5, 30) x = angleHOH * np.pi / 180 / 2 pos = [[0, 0, 0], [0, rOH * np.cos(x), rOH * np.sin(x)], [0, rOH * np.cos(x), -rOH * np.sin(x)]] calc = TIP4P() self.h2o = Atoms('OH2', [(0, 0, 0), (r * cos(a), 0, r * sin(a)), (r, 0, 0)]) vol = ((18.01528 / 6.022140857e23) / (0.9982 / 1e24))**(1 / 3.) self.h2o.set_cell((1.1 * r, 1.1 * r, 1.1 * r)) self.h2o = self.h2o.repeat(repeat) self.h2o.set_cell((0, 0, 0)) nmol = int(self.h2o.positions.shape[0] / 3) bonds = [(3 * i, 3 * i + 1) for i in range(nmol)] for i in range(nmol): bonds.append((3 * i, 3 * i + 2)) bonds_rOH = [] for bond in bonds: bonds_rOH.append((rOH, (bond))) angles = [(a, (3 * i + 2, 3 * i, 3 * i + 1)) for i in range(nmol)] self.h2o.constraints = FixInternals(bonds=bonds_rOH, angles=angles, epsilon=epsilon0) calc = TIP4P() self.h2o.calc = calc
def opt(self, rdkmol, dhls, logger='optlog.out'): Na = rdkmol.GetNumAtoms() X, S = __convert_rdkitmol_to_nparr__(rdkmol) atm=Atoms(symbols=S, positions=X) atm.set_calculator(ANIENS(self.ens)) #Set the ANI Ensemble as the calculator phi_fix = [] for d in dhls: phi_restraint=atm.get_dihedral(d) phi_fix.append([phi_restraint, d]) c = FixInternals(dihedrals=phi_fix, epsilon=1.e-9) atm.set_constraint(c) dyn = LBFGS(atm, logfile=logger) #Choose optimization algorith dyn.run(fmax=self.fmax, steps=1000) #optimize molecule to Gaussian's Opt=Tight fmax criteria, input in eV/A (I think) e=atm.get_potential_energy()*hdt.evtokcal s=atm.calc.stddev*hdt.evtokcal phi_value = [] for d in dhls: phi_value.append(atm.get_dihedral(d)*180./np.pi) #X = mol.get_positions() if self.printer: print('Phi value (degrees), energy (kcal/mol), sigma= ', phi_value, "{0:.2f}".format(e), "{0:.2f}".format(s)) return phi_value, e, s, atm
def add_tip4p_const(water): nmol = int(water.positions.shape[0] / 3) a = angleHOH * pi / 180 bonds = [(3 * i, 3 * i + 1) for i in range(nmol)] for i in range(nmol): bonds.append((3 * i, 3 * i + 2)) bonds_rOH = [] for bond in bonds: bonds_rOH.append((rOH, (bond))) angles = [(a, (3 * i + 2, 3 * i, 3 * i + 1)) for i in range(nmol)] water.constraints = FixInternals(bonds=bonds_rOH, angles=angles, epsilon=epsilon0) calc = TIP4P() water.calc = calc return water
def PreventScramblingConstraint(graph, atoms, double_bond_protection=False, fix_angles=False): ''' graph: NetworkX graph of the molecule atoms: ASE atoms object return: FixInternals constraint to apply to ASE calculations ''' angles_deg = None if fix_angles: allpaths = [] for node in graph: allpaths.extend(findPaths(graph, node, 2)) allpaths = {tuple(sorted(path)) for path in allpaths} angles_deg = [] for path in allpaths: angles_deg.append([atoms.get_angle(*path), list(path)]) bonds = [] for bond in [[a, b] for a, b in graph.edges if a != b]: bonds.append([atoms.get_distance(*bond), bond]) dihedrals_deg = None if double_bond_protection: double_bonds = get_double_bonds_indexes(atoms.positions, atoms.get_atomic_numbers()) if double_bonds != []: dihedrals_deg = [] for a, b in double_bonds: n_a = neighbors(graph, a) n_a.remove(b) n_b = neighbors(graph, b) n_b.remove(a) d = [n_a[0], a, b, n_b[0]] dihedrals_deg.append([atoms.get_dihedral(*d), d]) return FixInternals(dihedrals_deg=dihedrals_deg, angles_deg=angles_deg, bonds=bonds, epsilon=1)
def _copy_and_set_constraints(atoms, n_atoms_per_molecule, rigid_atoms, bonds, angles, dihedrals, hookean_lst): """Helper function for place_and_preoptimize_adsorbates Args: atoms (ase.Atoms) : conglomerate of adsorbate molecules n_atoms_per_molecule (int) : number of atoms per single molecule rigid_atoms (list) : indices of atoms to be fixed bonds (list) : each element contains a tuple of bondlength and atom indices angles (list) : each element contains a tuple of angle and atom indices dihedrals (list) : each element contains a tuple of dihedral and atom indices hookean_lst (list) : ase.constraints.Hookean bond constraints Returns: tuple : All indices of rigid_atoms, bonds, angles, dihedrals and hookean_lst are shifted by shift """ n_adsorbates = int(len(atoms) / n_atoms_per_molecule) all_rigid_atoms = [] all_bonds = [] all_hookeans = [] all_angles = [] all_dihedrals = [] for i in range(n_adsorbates): shift = i * n_atoms_per_molecule new_rigid_atoms, new_bonds, new_angles, new_dihedrals, new_hookean_lst = _shift_constraints(shift, rigid_atoms, bonds, angles, dihedrals, hookean_lst) all_rigid_atoms.extend(new_rigid_atoms) all_bonds.extend(new_bonds) all_angles.extend(new_angles) all_dihedrals.extend(new_dihedrals) all_hookeans.extend(new_hookean_lst) # too heavy!!! #fi = FixInternals(angles=all_angles, dihedrals=all_dihedrals, bonds=all_bonds, epsilon=1e-7) fi = FixInternals(angles=angles, dihedrals=dihedrals, bonds=bonds, epsilon=1e-7) fa = FixAtoms(all_rigid_atoms) # IMPORTANT! fix binding atom at the end, otherwise it moves! #atoms.set_constraint(hookean_lst.extend([fi, fa])) atoms.set_constraint([fi, fa]) return atoms
def test_getindices(): from ase.build import fcc111 from ase.constraints import (FixAtoms, FixBondLengths, FixLinearTriatomic, FixInternals, Hookean, constrained_indices) slab = fcc111('Pt', (4, 4, 4)) C1 = FixAtoms([0, 2, 4]) C2 = FixBondLengths([[0, 1], [0, 2]]) C3 = FixInternals(bonds=[[1, [7, 8]], [1, [8, 9]]]) C4 = Hookean(a1=30, a2=40, rt=1.79, k=5.) C5 = FixLinearTriatomic(triples=[(0, 1, 2), (3, 4, 5)]) slab.set_constraint([C1, C2, C3, C4, C5]) assert all( constrained_indices(slab, (FixAtoms, FixBondLengths)) == [0, 1, 2, 4]) assert all( constrained_indices(slab, (FixBondLengths, FixLinearTriatomic)) == [0, 1, 2, 3, 4, 5]) assert all( constrained_indices(slab) == [0, 1, 2, 3, 4, 5, 7, 8, 9, 30, 40])
def __init__(self, nmol=5, size=3): r = rSH a = angleHSH * pi / 180 # From https://doi.org/10.1063/1.445869 eexp = 6.24 * units.kcal / units.mol dexp = 2.75 aexp = 46 D = np.linspace(2.5, 3.5, 30) x = angleHSH * np.pi / 180 / 2 pos = [[0.00000000, 0.00000000, 0.10357400], [0.00000000, 0.97644400, -0.82858900], [0.00000000, -0.97644400, -0.82858900]] pos = pos * nmol calc = H2S() self.h2o = Atoms('SH2' * nmol, pos) disp = np.random.uniform(-size, size, (nmol, 3)) final_dis = [] for i in range(nmol): d = disp[i].tolist() for i in range(3): final_dis.append(d) self.h2o.set_positions(pos + np.array(final_dis)) #vol = ((18.01528 / 6.022140857e23) / (0.9982 / 1e24))**(1 / 3.) #self.h2o.set_cell((1.1*r , 1.1*r , 1.1*r )) #nmol = int(self.h2o.positions.shape[0] / 3) bonds = [(3 * i, 3 * i + 1) for i in range(nmol)] for i in range(nmol): bonds.append((3 * i, 3 * i + 2)) bonds_rSH = [] for bond in bonds: bonds_rSH.append((rSH, (bond))) angles = [(a, (3 * i + 2, 3 * i, 3 * i + 1)) for i in range(nmol)] self.h2o.constraints = FixInternals(bonds=bonds_rSH, angles=angles, epsilon=epsilon0) calc = H2S() self.h2o.calc = calc
def set_dihedrals_and_relax(atoms: Atoms, dihedrals: List[Tuple[float, DihedralInfo]]) -> float: """Set the dihedral angles and compute the energy of the system Args: atoms: Molecule to ajdust dihedrals: List of dihedrals to set to a certain angle Returns: Energy of the system """ # Copy input so that we can loop over it twice (i.e., avoiding problems around zip being a generator) dihedrals = list(dihedrals) # Set the dihedral angles to desired settings for di in dihedrals: atoms.set_dihedral(*di[1].chain, di[0], indices=di[1].group) # Define the constraints dih_cnsts = [(di[0], di[1].chain) for di in dihedrals] atoms.set_constraint() atoms.set_constraint(FixInternals(dihedrals_deg=dih_cnsts)) return relax_structure(atoms)
def iterate(atoms : Atoms): delta = 0.01 global bond1 global bond2 force = atoms.get_forces()[i['DIFFATOM']] bond1_dist = atoms.get_distance(i['DIFFATOM'], i['CONSATOM1'], vector=False) bond1_vector = atoms.get_distance(i['DIFFATOM'], i['CONSATOM1'], vector=True ) bond1_unit = bond1_vector / bond1_dist bond2_dist = atoms.get_distance(i['DIFFATOM'], i['CONSATOM2'], vector=False) bond2_vector = atoms.get_distance(i['DIFFATOM'], i['CONSATOM2'], vector=True ) bond2_unit = bond2_vector / bond2_dist bond1_projection = np.dot(force, bond1_unit) bond2_projection = np.dot(force, bond2_unit) if abs(bond1_projection) > bond2_projection: if bond1_projection > 0: print('Growing Bond 1') bond1 += delta bond2 -= delta / 4 else: print('Shrinking Bond 1') bond1 -= delta bond2 += delta / 4 else: if bond2_projection > 0: print('Growing Bond 2') bond2 += delta bond1 -= delta / 4 else: print('Shrinking Bond 2') bond2 -= delta bond1 += delta / 4 bonds = [ [ bond1, [ i['DIFFATOM'], i['CONSATOM1'] ] ], [ bond2, [ i['DIFFATOM'], i['CONSATOM2'] ] ] ] return FixInternals(bonds=bonds)
F = [] for d in D: dimer.positions[3:, 0] += d - dimer.positions[3, 0] E.append(dimer.get_potential_energy()) F.append(dimer.get_forces()) F = np.array(F) #plt.plot(D, E) F1 = np.polyval(np.polyder(np.polyfit(D, E, 7)), D) F2 = F[:, :3, 0].sum(1) error = abs(F1 - F2).max() dimer.constraints = FixInternals(bonds=[(r, (0, 1)), (r, (0, 2)), (r, (3, 4)), (r, (3, 5))], angles=[(a, (2, 0, 1)), (a, (5, 3, 4))]) opt = BFGS(dimer, trajectory=calc.name + '.traj', logfile=calc.name + 'd.log') opt.run(0.01) e0 = dimer.get_potential_energy() d0 = dimer.get_distance(0, 3) R = dimer.positions v1 = R[2] - R[3] v2 = R[3] - (R[5] + R[4]) / 2 a0 = np.arccos(np.dot(v1, v2) / (np.dot(v1, v1) * np.dot(v2, v2))**0.5) / np.pi * 180 fmt = '{0:>23}: {1:.3f} {2:.3f} {3:.3f} {4:.1f}' print(fmt.format(calc.name, -min(E), -e0, d0, a0))
b1 = [1.4, b1_ind] bond_list.append(b1) b1_ind = [3, 2] b1 = [1.26, b1_ind] bond_list.append(b1) b1_ind = [2, 0] b1 = [1.4, b1_ind] bond_list.append(b1) angle_list = [] #angle_ind = [3,2,0] #angle = [atoms.get_angle(angle_ind), angle_ind] #angle_list.append(angle) c = FixInternals(atoms, bonds=bond_list, angles=angle_list) #c = FixInternals(atoms, bonds=[b1, b2, b3], angles=[]) atoms.set_constraint(c) atoms.set_angle(indices2, (ang2 / 180.) * np.pi, mask=mask2) atoms.set_angle(indices3, (ang3 / 180.) * np.pi, mask=mask3) atoms.set_dihedral(indices1, (ang1 / 180.) * np.pi, mask=mask1) atoms.set_calculator(calc) omega = (atoms.get_dihedral(indices1) / np.pi) * 180. alpha = (atoms.get_angle(indices2) / np.pi) * 180. alpha2 = (atoms.get_angle(indices3) / np.pi) * 180. print omega, alpha, alpha2
# Fix this dihedral angle to whatever it was from the start indices = [6, 0, 1, 2] dihedral1 = system.get_dihedral(indices) # Fix angle to whatever it was from the start indices2 = [6, 0, 1] angle1 = system.get_angle(indices2) #system.set_dihedral(indices, pi/20, mask=[0,1,1,1,1,1,0,0,0]) # Fix bond between atoms 1 and 2 to 1.4 target_bondlength = 1.4 indices_bondlength = [1, 2] constraint = FixInternals(bonds=[(target_bondlength, indices_bondlength)], angles=[(angle1, indices2)], dihedrals=[(dihedral1, indices)], epsilon=1e-10) print(constraint) calc = EMT() opt = BFGS(system, trajectory='opt.traj', logfile='opt.log') previous_angle = system.get_angle(indices2) previous_dihedral = system.get_dihedral(indices) print('angle before', previous_angle) print('dihedral before', previous_dihedral) print('bond length before', system.get_distance(*indices_bondlength)) print('(target bondlength %s)', target_bondlength)
def test_turbomole_qmmm(): """Test the Turbomole calculator in simple QMMM and explicit interaction QMMM simulations.""" r = rOH a = angleHOH * pi / 180 D = np.linspace(2.5, 3.5, 30) interaction = LJInteractions({('O', 'O'): (epsilon0, sigma0)}) qm_par = {'esp fit': 'kollman', 'multiplicity': 1} for calc in [ TIP3P(), SimpleQMMM([0, 1, 2], Turbomole(**qm_par), TIP3P(), TIP3P()), SimpleQMMM([0, 1, 2], Turbomole(**qm_par), TIP3P(), TIP3P(), vacuum=3.0), EIQMMM([0, 1, 2], Turbomole(**qm_par), TIP3P(), interaction), EIQMMM([3, 4, 5], Turbomole(**qm_par), TIP3P(), interaction, vacuum=3.0), EIQMMM([0, 1, 2], Turbomole(**qm_par), TIP3P(), interaction, vacuum=3.0) ]: dimer = Atoms('H2OH2O', [(r * cos(a), 0, r * sin(a)), (r, 0, 0), (0, 0, 0), (r * cos(a / 2), r * sin(a / 2), 0), (r * cos(a / 2), -r * sin(a / 2), 0), (0, 0, 0)]) dimer.calc = calc E = [] F = [] for d in D: dimer.positions[3:, 0] += d - dimer.positions[5, 0] E.append(dimer.get_potential_energy()) F.append(dimer.get_forces()) F = np.array(F) F1 = np.polyval(np.polyder(np.polyfit(D, E, 7)), D) F2 = F[:, :3, 0].sum(1) error = abs(F1 - F2).max() assert error < 0.9 dimer.set_constraint( FixInternals(bonds=[(r, (0, 2)), (r, (1, 2)), (r, (3, 5)), (r, (4, 5))], angles_deg=[(angleHOH, (0, 2, 1)), (angleHOH, (3, 5, 4))])) opt = BFGS(dimer, trajectory=calc.name + '.traj', logfile=calc.name + 'd.log') opt.run(0.01) e0 = dimer.get_potential_energy() d0 = dimer.get_distance(2, 5) R = dimer.positions v1 = R[1] - R[5] v2 = R[5] - (R[3] + R[4]) / 2 a0 = np.arccos( np.dot(v1, v2) / (np.dot(v1, v1) * np.dot(v2, v2))**0.5) / np.pi * 180 fmt = '{0:>20}: {1:.3f} {2:.3f} {3:.3f} {4:.1f}' print(fmt.format(calc.name, -min(E), -e0, d0, a0))
from ase.build import fcc111 from ase.constraints import (FixAtoms, FixBondLengths, FixInternals, Hookean, constrained_indices) slab = fcc111('Pt', (4, 4, 4)) C1 = FixAtoms([0, 2, 4]) C2 = FixBondLengths([[0, 1], [0, 2]]) C3 = FixInternals(bonds=[[1, [7, 8]], [1, [8, 9]]]) C4 = Hookean(a1=30, a2=40, rt=1.79, k=5.) slab.set_constraint([C1, C2, C3, C4]) assert all( constrained_indices(slab, (FixAtoms, FixBondLengths)) == [0, 1, 2, 4]) assert all(constrained_indices(slab) == [0, 1, 2, 4, 7, 8, 9, 30, 40])
def test_dihedralconstraint(): from ase.calculators.emt import EMT from ase.constraints import FixInternals from ase.optimize.bfgs import BFGS from ase.build import molecule system = molecule('CH3CH2OH', vacuum=5.0) system.rattle(stdev=0.3) # Angles, Bonds, Dihedrals are built up with pairs of constraint # value and indices defining the constraint # Linear combinations of bond lengths are built up similarly with the # coefficients appended to the indices defining the constraint # Fix this dihedral angle to whatever it was from the start indices = [6, 0, 1, 2] dihedral1 = system.get_dihedral(*indices) # Fix angle to whatever it was from the start indices2 = [6, 0, 1] angle1 = system.get_angle(*indices2) # Fix bond between atoms 1 and 2 to 1.4 target_bondlength = 1.4 indices_bondlength = [1, 2] # Fix linear combination of two bond lengths with atom indices 0-8 and # 0-6 with weighting coefficients 1.0 and -1.0 to the current value. # In other words, fulfil the following constraint: # 1 * system.get_distance(0, 8) -1 * system.get_distance(0, 6) = const. bondcombo_def = [[0, 8, 1.0], [0, 6, -1.0]] def get_bondcombo(system, bondcombo_def): return sum([ defin[2] * system.get_distance(defin[0], defin[1]) for defin in bondcombo_def ]) target_bondcombo = get_bondcombo(system, bondcombo_def) constraint = FixInternals(bonds=[(target_bondlength, indices_bondlength)], angles_deg=[(angle1, indices2)], dihedrals_deg=[(dihedral1, indices)], bondcombos=[(target_bondcombo, bondcombo_def)], epsilon=1e-10) print(constraint) calc = EMT() opt = BFGS(system, trajectory='opt.traj', logfile='opt.log') previous_angle = system.get_angle(*indices2) previous_dihedral = system.get_dihedral(*indices) previous_bondcombo = get_bondcombo(system, bondcombo_def) print('angle before', previous_angle) print('dihedral before', previous_dihedral) print('bond length before', system.get_distance(*indices_bondlength)) print('(target bondlength %s)', target_bondlength) print('linear bondcombination before', previous_bondcombo) system.calc = calc system.set_constraint(constraint) print('-----Optimization-----') opt.run(fmax=0.01) new_angle = system.get_angle(*indices2) new_dihedral = system.get_dihedral(*indices) new_bondlength = system.get_distance(*indices_bondlength) new_bondcombo = get_bondcombo(system, bondcombo_def) print('angle after', new_angle) print('dihedral after', new_dihedral) print('bondlength after', new_bondlength) print('linear bondcombination after', new_bondcombo) err1 = new_angle - previous_angle err2 = new_dihedral - previous_dihedral err3 = new_bondlength - target_bondlength err4 = new_bondcombo - previous_bondcombo print('error in angle', repr(err1)) print('error in dihedral', repr(err2)) print('error in bondlength', repr(err3)) print('error in bondcombo', repr(err4)) assert err1 < 1e-11 assert err2 < 1e-12 assert err3 < 1e-12 assert err4 < 1e-12
def run_calculation(self, coords, label, calc_type="single_point", calculate_force=True, dihedral_freeze=None, ase_constraints=None): """ Method that runs an ASE calculation. Parameters ---------- coords : np.ndarray, shape=(n_atoms,3), dtype=float Coordinates array. label : str or int Label of the calculation. calc_type : str, default="single_point" Available options are "single_point" and "optimization". calculate_force : bool Whether to calculate forces. dihedral_freeze : list of list of int, default=None List of lists of wherein each inner list should contain 4 integers defining a torsion to be kept fixed. ase_constraints : list of ASE constraints, default=None List of ASE constraints to be applied during the scans. More information: https://wiki.fysik.dtu.dk/ase/ase/constraints.html Returns ------- coord : np.ndarray, shape=(n_atoms,3), dtype=float Coordinate array in nanometers. energy : float Energy value in kJ/mol. forces : np.ndarray, shape=(n_atoms,3), dtype=float Forces array, kJ/mol/nm. """ from ase.visualize import view # Change to calculation directory self._interface.chdir(self._calculation_dirs[label], absolute=True) # Reset ase calculator self._ase_calculator[int(label)].reset() # Set calculator in Atoms object atoms = ase.Atoms(self._symbols_string, coords) atoms.set_calculator(self._ase_calculator[label]) if not calculate_force: forces = None if self._cell is not None: # Set the cell and center the atoms atoms.set_cell(self._cell) atoms.center() if calc_type.lower() == "single_point": # Run calculation and extract potential energy and forces # Set calculator in Atoms object if calculate_force: forces = atoms.get_forces( ) * 96.48530749925794 * 10.0 # eV/A to kJ/mol/nm energy = atoms.get_potential_energy( ) * 96.48530749925794 # eV to kJ/mol # View molecule after sp calculation if self._view_atoms: view(atoms) # Go back to main folder self._interface.chdir_base() return energy, forces elif calc_type.lower() == "optimization": # Run optimization from ase.io import write, read logging.info("Performing QM optimization using ASE optimizer.") constraints_list = [] # Apply necessary dihedral constraints if dihedral_freeze is not None: dihedrals_to_fix = [] for dihedral in dihedral_freeze: dihedrals_to_fix.append([ atoms.get_dihedral(*dihedral) * np.pi / 180.0, dihedral ]) constraint = FixInternals(bonds=[], angles=[], dihedrals=dihedrals_to_fix, epsilon=self._shake_threshold) constraints_list.append(constraint) # Apply any ASE constraints # More information: https://wiki.fysik.dtu.dk/ase/ase/constraints.html if ase_constraints is not None: for constraint in ase_constraints: constraints_list.append(constraint) if len(constraints_list) > 0: atoms.set_constraint(constraints_list) opt = self._optimizer(atoms, trajectory=self._opt_traj_prefix + ".traj", logfile=self._opt_logfile) opt.run(fmax=self._opt_fmax) if dihedral_freeze is not None: del atoms.constraints # View molecule after optimization if self._view_atoms: view(atoms) if calculate_force: forces = atoms.get_forces( ) * 96.48530749925794 * 10.0 # eV/A to kJ/mol/nm # Get data coords = atoms.get_positions() * 0.1 # Angstrom to nm energy = atoms.get_potential_energy( ) * 96.48530749925794 # eV to kJ/mol # Go back to main folder self._interface.chdir_base() return coords, energy, forces else: raise NotImplementedError( "Calculation of type {} is not implemented.".format(calc_type))
def test_qmmm(testdir): r = rOH a = angleHOH * pi / 180 # From https://doi.org/10.1063/1.445869 eexp = 6.50 * units.kcal / units.mol dexp = 2.74 aexp = 27 D = np.linspace(2.5, 3.5, 30) i = LJInteractions({('O', 'O'): (epsilon0, sigma0)}) # General LJ interaction object sigma_mm = np.array([0, 0, sigma0]) epsilon_mm = np.array([0, 0, epsilon0]) sigma_qm = np.array([0, 0, sigma0]) epsilon_qm = np.array([0, 0, epsilon0]) ig = LJInteractionsGeneral(sigma_qm, epsilon_qm, sigma_mm, epsilon_mm, 3) for calc in [ TIP3P(), SimpleQMMM([0, 1, 2], TIP3P(), TIP3P(), TIP3P()), SimpleQMMM([0, 1, 2], TIP3P(), TIP3P(), TIP3P(), vacuum=3.0), EIQMMM([0, 1, 2], TIP3P(), TIP3P(), i), EIQMMM([3, 4, 5], TIP3P(), TIP3P(), i, vacuum=3.0), EIQMMM([0, 1, 2], TIP3P(), TIP3P(), i, vacuum=3.0), EIQMMM([0, 1, 2], TIP3P(), TIP3P(), ig), EIQMMM([3, 4, 5], TIP3P(), TIP3P(), ig, vacuum=3.0), EIQMMM([0, 1, 2], TIP3P(), TIP3P(), ig, vacuum=3.0) ]: dimer = Atoms('H2OH2O', [(r * cos(a), 0, r * sin(a)), (r, 0, 0), (0, 0, 0), (r * cos(a / 2), r * sin(a / 2), 0), (r * cos(a / 2), -r * sin(a / 2), 0), (0, 0, 0)]) dimer.calc = calc E = [] F = [] for d in D: dimer.positions[3:, 0] += d - dimer.positions[5, 0] E.append(dimer.get_potential_energy()) F.append(dimer.get_forces()) F = np.array(F) F1 = np.polyval(np.polyder(np.polyfit(D, E, 7)), D) F2 = F[:, :3, 0].sum(1) error = abs(F1 - F2).max() assert error < 0.01 dimer.constraints = FixInternals(bonds=[(r, (0, 2)), (r, (1, 2)), (r, (3, 5)), (r, (4, 5))], angles_deg=[ (np.degrees(a), (0, 2, 1)), (np.degrees(a), (3, 5, 4)) ]) opt = GPMin(dimer, trajectory=calc.name + '.traj', logfile=calc.name + 'd.log') opt.run(0.01) e0 = dimer.get_potential_energy() d0 = dimer.get_distance(2, 5) R = dimer.positions v1 = R[1] - R[5] v2 = R[5] - (R[3] + R[4]) / 2 a0 = np.arccos( np.dot(v1, v2) / (np.dot(v1, v1) * np.dot(v2, v2))**0.5) / np.pi * 180 fmt = '{0:>20}: {1:.3f} {2:.3f} {3:.3f} {4:.1f}' print(fmt.format(calc.name, -min(E), -e0, d0, a0)) assert abs(e0 + eexp) < 0.002 assert abs(d0 - dexp) < 0.01 assert abs(a0 - aexp) < 4 print(fmt.format('reference', 9.999, eexp, dexp, aexp))
def ase_scan(embedder, coords, atomnos, indexes, degrees=10, steps=36, relaxed=True, ad_libitum=False, indexes_to_be_moved=None, title='temp scan', logfile=None): ''' if ad libitum, steps is the minimum number of performed steps ''' assert len(indexes) == 4 if ad_libitum: if not relaxed: raise Exception(f'The ad_libitum keyword is only available for relaxed scans.') atoms = Atoms(atomnos, positions=coords) structures, energies = [], [] atoms.calc = get_ase_calc(embedder) if indexes_to_be_moved is None: indexes_to_be_moved = range(len(atomnos)) mask = np.array([i in indexes_to_be_moved for i, _ in enumerate(atomnos)], dtype=bool) t_start = time() if logfile is not None: logfile.write(f' > {title}\n') for scan_step in range(1000): loadbar_title = f'{title} - step {scan_step+1}' if ad_libitum: print(loadbar_title, end='\r') else: loadbar_title += '/'+str(steps) loadbar(scan_step+1, steps, loadbar_title+' '*(29-len(loadbar_title))) if logfile is not None: t_start_step = time() if relaxed: atoms.set_constraint(FixInternals(dihedrals_deg=[[atoms.get_dihedral(*indexes), indexes]])) with LBFGS(atoms, maxstep=0.2, logfile=None, trajectory=None) as opt: try: opt.run(fmax=0.05, steps=500) exit_str = 'converged' except ValueError: # Shake did not converge exit_str = 'crashed' iterations = opt.nsteps energies.append(atoms.get_total_energy() * 23.06054194532933) # eV to kcal/mol if logfile is not None: elapsed = time() - t_start_step s = '/' + str(steps) if not ad_libitum else '' logfile.write(f' Step {scan_step+1}{s} - {exit_str} - {iterations} iterations ({time_to_string(elapsed)})\n') structures.append(atoms.get_positions()) atoms.rotate_dihedral(*indexes, angle=degrees, mask=mask) if exit_str == 'crashed': break elif scan_step+1 >= steps: if ad_libitum: if any(( (max(energies) - energies[-1]) > 1, (max(energies) - energies[-1]) > max(energies)-energies[0], (energies[-1] - min(energies)) > 50 )): # ad_libitum stops when one of these conditions is met: # - we surpassed and are below the maximum of at least 1 kcal/mol # - we surpassed maximum and are below starting point # - current step energy is more than 50 kcal/mol above starting point print(loadbar_title) break else: break structures = np.array(structures) clean_directory() if logfile is not None: elapsed = time() - t_start logfile.write(f'{title} - completed ({time_to_string(elapsed)})\n') return align_structures(structures, indexes), energies
if len(f) == 4: dih = mol.get_dihedral(f[0] - 1, f[1] - 1, f[2] - 1, f[3] - 1) #ase enumerates atoms starting from zero dih *= pi / 180. dihedrals.append([dih, [f[0] - 1, f[1] - 1, f[2] - 1, f[3] - 1]]) for c in change: if len(c) == 3: bonds.append([c[2], [c[0] - 1, c[1] - 1]]) if len(c) == 4: angle = c[3] * pi / 180. angles.append([angle, [c[0] - 1, c[1] - 1, c[2] - 1]]) if len(c) == 5: dih = c[4] * pi / 180. dihedrals.append([dih, [c[0] - 1, c[1] - 1, c[2] - 1, c[3] - 1]]) cons = FixInternals(bonds=bonds, angles=angles, dihedrals=dihedrals) mol.set_constraint(cons) cons.adjust_positions(mol, mol.positions) try: dyn = BFGS(mol, trajectory='%s.traj' % label) dyn.run(fmax=0.05) db = connect('kinbot.db') db.write(mol, name=label, data={{'status': 'normal'}}) except RuntimeError, e: print 'error' db = connect('kinbot.db') db.write(mol, name=label, data={{'status': 'error'}}) """ try:
def test_dihedralconstraint(): from math import pi from ase.calculators.emt import EMT from ase.constraints import FixInternals from ase.optimize.bfgs import BFGS from ase.build import molecule system = molecule('CH3CH2OH', vacuum=5.0) system.rattle(stdev=0.3) # Angles, Bonds, Dihedrals are built up with pairs of constraint # value and indices defining the constraint # Fix this dihedral angle to whatever it was from the start indices = [6, 0, 1, 2] dihedral1 = system.get_dihedral(*indices) # Fix angle to whatever it was from the start indices2 = [6, 0, 1] angle1 = system.get_angle(*indices2) # system.set_dihedral(indices, pi/20, mask=[0,1,1,1,1,1,0,0,0]) # Fix bond between atoms 1 and 2 to 1.4 target_bondlength = 1.4 indices_bondlength = [1, 2] constraint = FixInternals(bonds=[(target_bondlength, indices_bondlength)], angles=[(angle1 * pi / 180, indices2)], dihedrals=[(dihedral1 * pi / 180, indices)], epsilon=1e-10) print(constraint) calc = EMT() opt = BFGS(system, trajectory='opt.traj', logfile='opt.log') previous_angle = system.get_angle(*indices2) previous_dihedral = system.get_dihedral(*indices) print('angle before', previous_angle) print('dihedral before', previous_dihedral) print('bond length before', system.get_distance(*indices_bondlength)) print('(target bondlength %s)', target_bondlength) system.set_calculator(calc) system.set_constraint(constraint) print('-----Optimization-----') opt.run(fmax=0.01) new_angle = system.get_angle(*indices2) new_dihedral = system.get_dihedral(*indices) new_bondlength = system.get_distance(*indices_bondlength) print('angle after', new_angle) print('dihedral after', new_dihedral) print('bondlength after', new_bondlength) err1 = new_angle - previous_angle err2 = new_dihedral - previous_dihedral err3 = new_bondlength - target_bondlength print('error in angle', repr(err1)) print('error in dihedral', repr(err2)) print('error in bondlength', repr(err3)) assert err1 < 1e-11 assert err2 < 1e-12 assert err3 < 1e-12
F = [] for d in D: dimer.positions[3:, 0] += d - dimer.positions[5, 0] E.append(dimer.get_potential_energy()) F.append(dimer.get_forces()) F = np.array(F) # plt.plot(D, E) F1 = np.polyval(np.polyder(np.polyfit(D, E, 7)), D) F2 = F[:, :3, 0].sum(1) error = abs(F1 - F2).max() dimer.constraints = FixInternals( bonds=[(r, (0, 2)), (r, (1, 2)), (r, (3, 5)), (r, (4, 5))], angles=[(a, (0, 2, 1)), (a, (3, 5, 4))]) opt = GPMin(dimer, trajectory=calc.name + '.traj', logfile=calc.name + 'd.log') opt.run(0.01) e0 = dimer.get_potential_energy() d0 = dimer.get_distance(2, 5) R = dimer.positions v1 = R[1] - R[5] v2 = R[5] - (R[3] + R[4]) / 2 a0 = np.arccos(np.dot(v1, v2) / (np.dot(v1, v1) * np.dot(v2, v2))**0.5) / np.pi * 180 fmt = '{0:>20}: {1:.3f} {2:.3f} {3:.3f} {4:.1f}' print(fmt.format(calc.name, -min(E), -e0, d0, a0)) assert abs(e0 + eexp) < 0.002
def run_md(self, coords, label, steps=None, dt=None, initial_temperature=None, integrator=None, integrator_args=None, dihedral_freeze=None, ase_constraints=None): """ Method that runs an ASE calculation. Parameters ---------- coords : np.ndarray, shape=(n_atoms,3), dtype=float Coordinates array. label : str or int Label of the calculation. steps: int Number of steps to take. dt : unit.Quantity MD Time step. initial_temperature : float with ase.unit Temperature of MD. integrator : ase.md integrator ASE integrator. integrator_args : dict Additional integrator options. dihedral_freeze : list of list of int, default=None List of lists of wherein each inner list should contain 4 integers defining a torsion to be kept fixed. ase_constraints : list of ASE constraints, default=None List of ASE constraints to be applied during the scans. More information: https://wiki.fysik.dtu.dk/ase/ase/constraints.html Returns ------- coord : np.ndarray, shape=(n_atoms,3), dtype=float Coordinate array in nanometers. energy : float Energy value in kJ/mol. forces : np.ndarray, shape=(n_atoms,3), dtype=float Forces array, kJ/mol/nm. """ from ase.md.velocitydistribution import MaxwellBoltzmannDistribution if steps is None: assert self._md_steps is not None steps = self._md_steps if dt is None: assert self._md_dt is not None dt = self._md_dt if initial_temperature is None: assert self._md_initial_temperature is not None initial_temperature = self._md_initial_temperature if integrator is None: assert self._md_integrator is not None integrator = self._md_integrator if integrator_args is None: assert self._md_integrator_args is not None integrator_args = self._md_integrator_args # Change to calculation directory self._interface.chdir(self._calculation_dirs[label], absolute=True) # Reset ase calculator self._ase_calculator[label].reset() # Set calculator in Atoms object atoms = ase.Atoms(self._symbols_string, coords) atoms.set_calculator(self._ase_calculator[label]) if self._cell is not None: # Set the cell and center the atoms atoms.set_cell(self._cell) atoms.center() constraints_list = [] # Apply necessary dihedral constraints if dihedral_freeze is not None: dihedrals_to_fix = [] for dihedral in dihedral_freeze: dihedrals_to_fix.append( [atoms.get_dihedral(*dihedral) * np.pi / 180.0, dihedral]) constraint = FixInternals(bonds=[], angles=[], dihedrals=dihedrals_to_fix, epsilon=self._shake_threshold) constraints_list.append(constraint) # Apply any ASE constraints # More information: https://wiki.fysik.dtu.dk/ase/ase/constraints.html if ase_constraints is not None: for constraint in ase_constraints: constraints_list.append(constraint) if len(constraints_list) > 0: atoms.set_constraint(constraints_list) # Randomize velocities MaxwellBoltzmannDistribution(atoms, initial_temperature, force_temp=True, rng=np.random) # Get data forces_initial = atoms.get_forces( ) * 96.48530749925794 * 10.0 # eV/A to kJ/mol/nm kinetic_intial = atoms.get_kinetic_energy( ) * 96.48530749925794 # eV to kJ/mol potential_inital = atoms.get_potential_energy( ) * 96.48530749925794 # eV to kJ/mol dyn = integrator( atoms, dt, *integrator_args, trajectory=self._opt_traj_prefix + ".traj", logfile=self._opt_logfile, ) dyn.run(steps) # Get data forces_final = atoms.get_forces( ) * 96.48530749925794 * 10.0 # eV/A to kJ/mol/nm coords = atoms.get_positions() * 0.1 # Angstrom to nm kinetic_final = atoms.get_kinetic_energy( ) * 96.48530749925794 # eV to kJ/mol potential_final = atoms.get_potential_energy( ) * 96.48530749925794 # eV to kJ/mol # View molecule after sp calculation self._interface.chdir_base() return coords, potential_inital, kinetic_intial, forces_initial, potential_final, kinetic_final, forces_final
#NOTE: you will need to set a sensible calculator for this to give results! molecule.set_calculator(EMT()) deloc = Delocalizer(molecule) assert 0 #constraining all stretches with FixInternals stretch_ind = deloc.ic.getStretchBendTorsOop()[0][0] stretches = deloc.ic.getStretchBendTorsOop()[1] bonds = [] for i in range(len(stretch_ind)): i, j = stretches[i] - 1 print i, j bonds.append([molecule.get_distance(i, j), [i, j]]) f = FixInternals(bonds=bonds, angles=[], dihedrals=[]) #molecule.set_constraint(f) bh = BetterHopping(atoms=molecule, temperature=100 * kB, dr=1.0, optimizer=BFGS, fmax=0.025, logfile='tt.log', maxmoves=50, movemode=1, numdelocmodes=20, constrain=True) bh.run(50)
def test_qmmm_tip4p(): from math import cos, sin, pi import numpy as np #import matplotlib.pyplot as plt import ase.units as units from ase import Atoms from ase.calculators.tip4p import TIP4P, epsilon0, sigma0, rOH, angleHOH from ase.calculators.qmmm import (SimpleQMMM, EIQMMM, LJInteractions, LJInteractionsGeneral) from ase.constraints import FixInternals from ase.optimize import BFGS r = rOH a = angleHOH * pi / 180 # From https://doi.org/10.1063/1.445869 eexp = 6.24 * units.kcal / units.mol dexp = 2.75 aexp = 46 D = np.linspace(2.5, 3.5, 30) i = LJInteractions({('O', 'O'): (epsilon0, sigma0)}) # General LJ interaction object sigma_mm = np.array([sigma0, 0, 0]) epsilon_mm = np.array([epsilon0, 0, 0]) sigma_qm = np.array([sigma0, 0, 0]) epsilon_qm = np.array([epsilon0, 0, 0]) ig = LJInteractionsGeneral(sigma_qm, epsilon_qm, sigma_mm, epsilon_mm, 3) for calc in [ TIP4P(), SimpleQMMM([0, 1, 2], TIP4P(), TIP4P(), TIP4P()), SimpleQMMM([0, 1, 2], TIP4P(), TIP4P(), TIP4P(), vacuum=3.0), EIQMMM([0, 1, 2], TIP4P(), TIP4P(), i), EIQMMM([3, 4, 5], TIP4P(), TIP4P(), i, vacuum=3.0), EIQMMM([0, 1, 2], TIP4P(), TIP4P(), i, vacuum=3.0), EIQMMM([0, 1, 2], TIP4P(), TIP4P(), ig), EIQMMM([3, 4, 5], TIP4P(), TIP4P(), ig, vacuum=3.0), EIQMMM([0, 1, 2], TIP4P(), TIP4P(), ig, vacuum=3.0) ]: dimer = Atoms('OH2OH2', [(0, 0, 0), (r * cos(a), 0, r * sin(a)), (r, 0, 0), (0, 0, 0), (r * cos(a / 2), r * sin(a / 2), 0), (r * cos(a / 2), -r * sin(a / 2), 0)]) dimer.calc = calc E = [] F = [] for d in D: dimer.positions[3:, 0] += d - dimer.positions[3, 0] E.append(dimer.get_potential_energy()) F.append(dimer.get_forces()) F = np.array(F) #plt.plot(D, E) F1 = np.polyval(np.polyder(np.polyfit(D, E, 7)), D) F2 = F[:, :3, 0].sum(1) error = abs(F1 - F2).max() assert error < 0.01 dimer.constraints = FixInternals(bonds=[(r, (0, 1)), (r, (0, 2)), (r, (3, 4)), (r, (3, 5))], angles=[(a, (2, 0, 1)), (a, (5, 3, 4))]) opt = BFGS(dimer, maxstep=0.04, trajectory=calc.name + '.traj', logfile=calc.name + 'd.log') opt.run(0.01) e0 = dimer.get_potential_energy() d0 = dimer.get_distance(0, 3) R = dimer.positions v1 = R[2] - R[3] v2 = R[3] - (R[5] + R[4]) / 2 a0 = np.arccos( np.dot(v1, v2) / (np.dot(v1, v1) * np.dot(v2, v2))**0.5) / np.pi * 180 fmt = '{0:>23}: {1:.3f} {2:.3f} {3:.3f} {4:.1f}' print(fmt.format(calc.name, -min(E), -e0, d0, a0)) assert abs(e0 + eexp) < 0.002 assert abs(d0 - dexp) < 0.006 assert abs(a0 - aexp) < 2.5 print(fmt.format('reference', 9.999, eexp, dexp, aexp))
if 'CONSATOM3' in i and 'CONTINUE_3PT' in i: from Classes_ASE import LockedTo3AtomPlane print('3 Atom Constraint cont.') a = read('POSCAR')# type: Atoms c = LockedTo3AtomPlane(i['DIFFATOM'], (i['CONSATOM1'], i['CONSATOM2'], i['CONSATOM3']), a.positions[i['DIFFATOM']]) elif 'CONSTYPE' in i and i['CONSTYPE'] == 'Bond': if 'ITERATE' in i and i['ITERATE']: from ase.constraints import FixInternals print('Constraining Bonds to fixed length') bond1 = atoms.get_distance(i['DIFFATOM'], i['CONSATOM1'], mic=True) bond2 = atoms.get_distance(i['DIFFATOM'], i['CONSATOM2'], mic=True) bonds = [ [ bond1, [ i['DIFFATOM'], i['CONSATOM1'] ] ], [ bond2, [ i['DIFFATOM'], i['CONSATOM2'] ] ] ] c = FixInternals(bonds=bonds) def iterate(atoms : Atoms): delta = 0.01 global bond1 global bond2 force = atoms.get_forces()[i['DIFFATOM']] bond1_dist = atoms.get_distance(i['DIFFATOM'], i['CONSATOM1'], vector=False) bond1_vector = atoms.get_distance(i['DIFFATOM'], i['CONSATOM1'], vector=True ) bond1_unit = bond1_vector / bond1_dist bond2_dist = atoms.get_distance(i['DIFFATOM'], i['CONSATOM2'], vector=False) bond2_vector = atoms.get_distance(i['DIFFATOM'], i['CONSATOM2'], vector=True ) bond2_unit = bond2_vector / bond2_dist bond1_projection = np.dot(force, bond1_unit) bond2_projection = np.dot(force, bond2_unit) if abs(bond1_projection) > bond2_projection: