def test_emt1(): from ase import Atoms from ase.calculators.emt import EMT from ase.constraints import FixBondLength from ase.io import Trajectory from ase.optimize import BFGS a = 3.6 b = a / 2 cu = Atoms('Cu2Ag', positions=[(0, 0, 0), (b, b, 0), (a, a, b)], calculator=EMT()) e0 = cu.get_potential_energy() print(e0) d0 = cu.get_distance(0, 1) cu.set_constraint(FixBondLength(0, 1)) t = Trajectory('cu2ag.traj', 'w', cu) qn = BFGS(cu) qn.attach(t.write) def f(): print(cu.get_distance(0, 1)) qn.attach(f) qn.run(fmax=0.001) assert abs(cu.get_distance(0, 1) - d0) < 1e-14
def get_fingerprint(Optimizer, indiv, binsize, cutoffdist): """Function to calculate the fingerprint of a structure""" rs = numpy.linspace(0.0, cutoffdist, cutoffdist/binsize) indi = indiv[0] Vuc = indi.get_volume() if Optimizer.structure == 'Defect': solid = Atoms() solid.extend(indi) solid.extend(indiv.bulki) elif Optimizer.structure == 'Crystal': solid = indi.repeat([3, 3, 3]) else: solid = indi.copy() syms = sorted(list(set([atm.symbol for atm in solid]))) fingerprints = [] for i in range(len(syms)): for j in range(i, len(syms)): indl = [atm for atm in indi if atm.symbol == syms[i]] ind = Atoms() for one in indl: ind.append(one) soll = [atm for atm in solid if atm.symbol == syms[j]] sol = Atoms() for one in soll: sol.append(one) soli = [atm for atm in solid if atm.symbol == syms[i]] value = [] for R in rs: value2 = [] for k in range(len(ind)): value1 = [] for m in range(len(sol)): if k != m: rij = sol.get_distance(k, m, mic = True) if rij == 0: pass #pdb.set_trace() value1.append(dirac(R, a=rij, sig=0.02) * 1. / (4*math.pi * rij** 2*binsize * len(soli) * len(sol) / Vuc)) value2.append(sum(value1)) value.append(sum(value2)) fingerprints.append(value) fpt = [] for one in fingerprints: fpt.extend(one) return fpt
def voids(atoms_in): """Find location and size of voids in a given structure. Returns the voids as 'X' atoms. The atoms' charge is misused to contain the voids' radius. """ trials = 6 # XXX do not hardwire atoms = atoms_in.copy() # append moving atom atoms.append(Atom('X')) atoms.set_calculator(RepulsivePotential()) voids_a = Atoms() voids_a.set_cell(atoms.get_cell()) voids_a.set_pbc(atoms.get_pbc()) positions = atoms.get_positions() for pos in positions[:-1]: for c in range(trials): positions[-1] = pos + 0.1 * np.random.uniform(-1, 1, size=3) atoms.set_positions(positions) # XXX do not hardwire relax = FIRE(atoms, logfile=None ) # XXX do not hardwire relax.run(fmax=0.001, steps=100) # get minimal distance Rmin = 100000 for b in range(len(atoms) - 1): R = atoms.get_distance(b, -1, mic=True) if R < Rmin: Rmin = R # check if new or better voids_a.append(Atom('X', atoms.get_positions()[-1], charge=Rmin)) voids_a.set_positions(voids_a.get_positions(wrap=True)) remove = [] last = len(voids_a) - 1 for ia, a in enumerate(voids_a[:-1]): d = voids_a.get_distance(ia, -1, mic=True) if d < a.charge or d < Rmin: if a.charge > Rmin: remove.append(last) else: remove.append(ia) remove.sort() if last not in remove: p = voids_a.get_positions()[-1] print('found new void at [%g,%g,%g], R=%g' % (p[0], p[1], p[2], Rmin)) for a in remove[::-1]: if a != last: p = voids_a.get_positions()[a] print('removing void at [%g,%g,%g], R=%g' % (p[0], p[1], p[2], voids_a[a].charge)) voids_a.pop(a) return voids_a
def check_min_dist(Optimizer, totalsol, type='Defect', nat=None, min_len=0.7, STR=''): if type=='Defect' or type=='Crystal' or type=='Surface': if nat==None: nat=len(totalsol) cutoffs=[2.0 for one in totalsol] nl=NeighborList(cutoffs,bothways=True,self_interaction=False) nl.update(totalsol) for one in totalsol[0:nat]: nbatoms=Atoms() nbatoms.append(one) indices, offsets=nl.get_neighbors(one.index) for index, d in zip(indices,offsets): index = int(index) sym=totalsol[index].symbol pos=totalsol[index].position + numpy.dot(d,totalsol.get_cell()) at=Atom(symbol=sym,position=pos) nbatoms.append(at) while True: dflag=False for i in range(1,len(nbatoms)): d=nbatoms.get_distance(0,i) if d < min_len: nbatoms.set_distance(0,i,min_len+.01,fix=0.5) STR+='--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n' dflag=True if dflag==False: break for i in range(len(indices)): totalsol[indices[i]].position=nbatoms[i+1].position totalsol[one.index].position=nbatoms[0].position nl.update(totalsol) elif type=='Cluster': if not 'LAMMPS' in Optimizer.modules: for i in range(len(totalsol)): for j in range(len(totalsol)): if i != j: d=totalsol.get_distance(i,j) if d < min_len: totalsol.set_distance(i,j,min_len,fix=0.5) STR+='--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n' else: rank = MPI.COMM_WORLD.Get_rank() #logger = logging.getLogger(Optimizer.loggername) R = totalsol.arrays['positions'] tol = 0.01 epsilon = 0.05 fix = 0.5 closelist = numpy.arange(len(totalsol)) iter = 0 while len(closelist) > 0 and iter<2: iter+=1 closelist = [] dist=spatial.distance.cdist(R,R) numpy.fill_diagonal(dist,1.0) smalldist = numpy.where(dist < min_len-tol) for ind in range(len(smalldist[0])): i = smalldist[0][ind] j = smalldist[1][ind] if i < j and dist[i][j] < min_len-tol: closelist.append(i) closelist.append(j) if dist[i][j] > epsilon: x = 1.0 - min_len / dist[i][j] D = R[j]-R[i] R[i] += (x * fix) * D R[j] -= (x * (1.0 - fix)) * D else: R[i] += [0.2, 0.0, 0.0] R[j] -= [0.2, 0.0, 0.0] R2P = [R[i],R[j]] dist2P=spatial.distance.cdist(R2P,R) dist[i] = dist2P[0] dist[j] = dist2P[1] for k in range(len(R)): dist[k][i] = dist[i][k] dist[k][j] = dist[j][k] closelist=list(set(closelist)) closelist.sort() #if len(closelist) != 0: # logger.info('M:iter {0}, closelist size {1}'.format(iter,len(closelist))) else: print 'WARNING: In Check_Min_Dist in EvalEnergy: Structure Type not recognized' return totalsol, STR
# 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 assert abs(d0 - dexp) < 0.01 assert abs(a0 - aexp) < 4 print(fmt.format('reference', 9.999, eexp, dexp, aexp)) # plt.show()
def test_au111(wrap): zpos = cos(134.3 / 2.0 * pi / 180.0) * 1.197 xpos = sin(134.3 / 2.0 * pi / 180.0) * 1.19 co2 = Atoms('COO', positions=[(-xpos + 1.2, 0, -zpos), (-xpos + 1.2, -1.1, -zpos), (-xpos + 1.2, 1.1, -zpos)]) slab = fcc111('Au', size=(2, 2, 4), vacuum=2 * 5, orthogonal=True) slab.center() add_adsorbate(slab, co2, 1.5, 'bridge') slab.set_pbc((True, True, False)) d0 = co2.get_distance(-3, -2) d1 = co2.get_distance(-3, -1) d2 = co2.get_distance(-2, -1) calc = EMT() slab.set_calculator(calc) if wrap: # Remap into the cell so bond is actually wrapped: slab.set_scaled_positions(slab.get_scaled_positions() % 1.0) constraint = FixLinearTriatomic(triples=[(-2, -3, -1)]) slab.set_constraint(constraint) dyn = BFGS(slab, trajectory='relax_%d.traj' % wrap) dyn.run(fmax=0.05) assert abs(slab.get_distance(-3, -2, mic=1) - d0) < 1e-9 assert abs(slab.get_distance(-3, -1, mic=1) - d1) < 1e-9 assert abs(slab.get_distance(-2, -1, mic=1) - d2) < 1e-9
def test_external_force(): """Tests for class ExternalForce in ase/constraints.py""" f_ext = 0.2 atom1 = 0 atom2 = 1 atom3 = 2 atoms = Atoms('H3', positions=[(0, 0, 0), (0.751, 0, 0), (0, 1., 0)]) atoms.calc = EMT() # Without external force optimize(atoms) dist1 = atoms.get_distance(atom1, atom2) # With external force con1 = ExternalForce(atom1, atom2, f_ext) atoms.set_constraint(con1) optimize(atoms) dist2 = atoms.get_distance(atom1, atom2) # Distance should increase due to the external force assert dist2 > dist1 # Combine ExternalForce with FixBondLength # Fix the bond on which the force acts con2 = FixBondLength(atom1, atom2) # ExternalForce constraint at the beginning of the list!!! atoms.set_constraint([con1, con2]) optimize(atoms) f_con = con2.constraint_forces # It was already optimized with this external force, therefore # the constraint force should be almost zero assert norm(f_con[0]) <= fmax # To get the complete constraint force (with external force), # use only the FixBondLength constraint, after the optimization with # ExternalForce atoms.set_constraint(con2) optimize(atoms) f_con = con2.constraint_forces[0] assert round(norm(f_con), 2) == round(abs(f_ext), 2) # Fix another bond and incrase the external force f_ext *= 2 con1 = ExternalForce(atom1, atom2, f_ext) d1 = atoms.get_distance(atom1, atom3) con2 = FixBondLength(atom1, atom3) # ExternalForce constraint at the beginning of the list!!! atoms.set_constraint([con1, con2]) optimize(atoms) d2 = atoms.get_distance(atom1, atom3) # Fixed distance should not change assert round(d1, 5) == round(d2, 5)
def test_CO2linear_Au111_langevin(testdir): """Test Langevin with constraints for rigid linear triatomic molecules""" rng = np.random.RandomState(0) eref = 3.133526 zpos = cos(134.3 / 2.0 * pi / 180.0) * 1.197 xpos = sin(134.3 / 2.0 * pi / 180.0) * 1.19 co2 = Atoms('COO', positions=[(-xpos + 1.2, 0, -zpos), (-xpos + 1.2, -1.1, -zpos), (-xpos + 1.2, 1.1, -zpos)]) slab = fcc111('Au', size=(2, 2, 4), vacuum=2 * 5, orthogonal=True) slab.center() add_adsorbate(slab, co2, 1.5, 'bridge') slab.set_pbc((True, True, False)) d0 = co2.get_distance(-3, -2) d1 = co2.get_distance(-3, -1) d2 = co2.get_distance(-2, -1) calc = EMT() slab.calc = calc constraint = FixLinearTriatomic(triples=[(-2, -3, -1)]) slab.set_constraint(constraint) fr = 0.1 dyn = Langevin(slab, 2.0 * units.fs, temperature_K=300, friction=fr, trajectory='langevin_%.1f.traj' % fr, logfile='langevin_%.1f.log' % fr, loginterval=20, rng=rng) dyn.run(100) # Check that the temperature is within a reasonable range T = slab.get_temperature() assert T > 100 assert T < 500 # Check that the constraints work assert abs(slab.get_distance(-3, -2, mic=1) - d0) < 1e-9 assert abs(slab.get_distance(-3, -1, mic=1) - d1) < 1e-9 assert abs(slab.get_distance(-2, -1, mic=1) - d2) < 1e-9 # If the energy differs from the reference energy # it is most probable that the redistribution of # random forces in Langevin is not working properly assert abs(slab.get_potential_energy() - eref) < 1e-4
def test_atom(): m = Atoms('H2') a = m[0] b = Atom('H') for c in [a, b]: assert c.x == 0 c.z = 24.0 assert c.position[2] == 24.0 assert c.symbol == 'H' c.number = 92 assert c.symbol == 'U' c.symbol = 'Fe' assert c.number == 26 c.tag = 42 assert c.tag == 42 c.momentum = (1, 2, 3) assert m[0].tag == 42 momenta = m.get_momenta() assert abs(momenta).sum() > 0 m = Atoms('LiH') for a in m: print(a.symbol) for a in m: if a.symbol == 'H': a.z = 0.75 assert m.get_distance(0, 1) == 0.75 a = m.pop() m += a del m[:1] print(m)
def test_qmmm_acn(): import numpy as np import ase.units as units from ase import Atoms from ase.calculators.acn import (ACN, m_me, r_cn, r_mec, sigma_me, sigma_c, sigma_n, epsilon_me, epsilon_c, epsilon_n) from ase.calculators.qmmm import SimpleQMMM, LJInteractionsGeneral, EIQMMM from ase.constraints import FixLinearTriatomic from ase.optimize import BFGS # From https://www.sciencedirect.com/science/article/pii/S0166128099002079 eref = 4.9 * units.kcal / units.mol dref = 3.368 aref = 79.1 sigma = np.array([sigma_me, sigma_c, sigma_n]) epsilon = np.array([epsilon_me, epsilon_c, epsilon_n]) inter = LJInteractionsGeneral(sigma, epsilon, sigma, epsilon, 3) for calc in [ACN(), SimpleQMMM([0, 1, 2], ACN(), ACN(), ACN()), SimpleQMMM([0, 1, 2], ACN(), ACN(), ACN(), vacuum=3.0), EIQMMM([0, 1, 2], ACN(), ACN(), inter), EIQMMM([0, 1, 2], ACN(), ACN(), inter, vacuum=3.0), EIQMMM([3, 4, 5], ACN(), ACN(), inter, vacuum=3.0)]: dimer = Atoms('CCNCCN', [(-r_mec, 0, 0), (0, 0, 0), (r_cn, 0, 0), (r_mec, 3.7, 0), (0, 3.7, 0), (-r_cn, 3.7, 0)]) masses = dimer.get_masses() masses[::3] = m_me dimer.set_masses(masses) dimer.calc = calc fixd = FixLinearTriatomic(triples=[(0, 1, 2), (3, 4, 5)]) dimer.set_constraint(fixd) opt = BFGS(dimer, maxstep=0.04, trajectory=calc.name + '.traj', logfile=calc.name + 'd.log') opt.run(0.001, steps=1000) e0 = dimer.get_potential_energy() d0 = dimer.get_distance(1, 4) a0 = dimer.get_angle(2, 1, 4) fmt = '{0:>25}: {1:.3f} {2:.3f} {3:.1f}' print(fmt.format(calc.name, -e0, d0, a0)) assert abs(e0 + eref) < 0.013 assert abs(d0 - dref) < 0.224 assert abs(a0 - aref) < 2.9 print(fmt.format('reference', eref, dref, aref))
def test_n2(): from ase import Atoms from ase.calculators.emt import EMT from ase.optimize import QuasiNewton n2 = Atoms('N2', positions=[(0, 0, 0), (0, 0, 1.1)], calculator=EMT()) QuasiNewton(n2).run(0.01) print(n2.get_distance(0, 1), n2.get_potential_energy())
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)
def get_fingerprint(Optimizer, indiv, binsize, cutoffdist): """Function to calculate the fingerprint of a structure """ rs = numpy.linspace(0.0, cutoffdist, cutoffdist / binsize) indi = indiv[0] Vuc = indi.get_volume() if Optimizer.structure == 'Defect': solid = Atoms() solid.extend(indi) solid.extend(indiv.bulki) elif Optimizer.structure == 'Crystal': solid = indi.repeat([3, 3, 3]) else: solid = indi.copy() syms = sorted(list(set([atm.symbol for atm in solid]))) fingerprints = [] for i in range(len(syms)): for j in range(i, len(syms)): indl = [atm for atm in indi if atm.symbol == syms[i]] ind = Atoms() for one in indl: ind.append(one) soll = [atm for atm in solid if atm.symbol == syms[j]] sol = Atoms() for one in soll: sol.append(one) soli = [atm for atm in solid if atm.symbol == syms[i]] value = [] for R in rs: value2 = [] for k in range(len(ind)): value1 = [] for m in range(len(sol)): if k != m: rij = sol.get_distance(k, m, mic=True) if rij == 0: pass #pdb.set_trace() value1.append( dirac(R, a=rij, sig=0.02) * 1 / (4 * math.pi * rij**2 * binsize * len(soli) * len(sol) / Vuc)) value2.append(sum(value1)) value.append(sum(value2)) fingerprints.append(value) fpt = [] for one in fingerprints: fpt.extend(one) return fpt
def check_min_dist(totalsol, type='Defect', nat=None, min_len=0.7, STR=''): if type == 'Defect' or type == 'Crystal' or type == 'Surface': if nat == None: nat = len(totalsol) cutoffs = [2.0 for one in totalsol] nl = NeighborList(cutoffs, bothways=True, self_interaction=False) nl.update(totalsol) for one in totalsol[0:nat]: nbatoms = Atoms() nbatoms.append(one) indices, offsets = nl.get_neighbors(one.index) for index, d in zip(indices, offsets): index = int(index) sym = totalsol[index].symbol pos = totalsol[index].position + numpy.dot( d, totalsol.get_cell()) at = Atom(symbol=sym, position=pos) nbatoms.append(at) while True: dflag = False for i in range(1, len(nbatoms)): d = nbatoms.get_distance(0, i) if d < min_len: nbatoms.set_distance(0, i, min_len + .01, fix=0.5) STR += '--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n' dflag = True if dflag == False: break for i in range(len(indices)): totalsol[indices[i]].position = nbatoms[i + 1].position totalsol[one.index].position = nbatoms[0].position nl.update(totalsol) elif type == 'Cluster': for i in range(len(totalsol)): for j in range(len(totalsol)): if i != j: d = totalsol.get_distance(i, j) if d < min_len: totalsol.set_distance(i, j, min_len, fix=0.5) STR += '--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n' else: print 'WARNING: In Check_Min_Dist in EvalEnergy: Structure Type not recognized' return totalsol, STR
def get_distances(indices, positions, rvecs=None): """Compute distances between particles""" atoms = Atoms(positions=positions, ) if rvecs is not None: cell = ase.geometry.Cell(rvecs) atoms.set_cell(cell) atoms.set_pbc(True) mic = True else: cell = None mic = False ndist = indices.shape[0] distances = np.zeros(ndist) for i in range(ndist): distances[i] = atoms.get_distance(indices[i, 0], indices[i, 1], mic=mic) return distances
def main(): arg = sys.argv inputfile = arg[1] outputfile = arg[2] atom1 = int(arg[3]) atom2 = int(arg[4]) f = open(inputfile) output = open(outputfile, 'w') atoms = None atom_output = Trajectory('trajectory.traj', 'w', atoms) iteration = 0 trajectories = [] a = 10.0 b = 10.0 c = 10.0 cell_atoms = np.array([[a, 0, 0], [0, b, 0], [0, 0, c]]) while True: coordinates = [] type_atoms = [] line = f.readline() if not line: break atom_numb = int(line.split()[0]) line = f.readline() for i in range(0, atom_numb): line = f.readline() fields = line.split() type_atoms.append(fields[0]) coordinates.append( [float(fields[1]), float(fields[2]), float(fields[3])]) traj = Atoms(np.array(type_atoms), positions=np.array(coordinates), cell=cell_atoms, pbc=True) traj.wrap() atom_output.write(traj) output.write("%6d %8.4f\n" % (iteration, traj.get_distance(atom1, atom2, mic=True))) iteration += 1
class AutoTST_Molecule(): """ A class that allows for one to create RMG, RDKit and ASE molecules from a single string with identical atom indicies Inputs: * smiles (str): a SMILES string that describes the molecule of interest * rmg_molecule (RMG Molecule object): an rmg molecule that we can extract information from """ def __init__(self, smiles=None, rmg_molecule=None): assert ( smiles or rmg_molecule ), "Please provide a SMILES string and / or an RMG Molecule object." if smiles and rmg_molecule: assert rmg_molecule.isIsomorphic( Molecule(SMILES=smiles )), "SMILES string did not match RMG Molecule object" self.smiles = smiles self.rmg_molecule = rmg_molecule elif rmg_molecule: self.rmg_molecule = rmg_molecule self.smiles = rmg_molecule.toSMILES() else: self.smiles = smiles self.rmg_molecule = Molecule(SMILES=smiles) self.get_rdkit_molecule() self.set_rmg_coords("RDKit") self.get_ase_molecule() self.get_torsions() self.get_angles() self.get_bonds() def __repr__(self): return '<AutoTST Molecule "{0}">'.format(self.smiles) def get_rdkit_molecule(self): """ A method to create an RDKit Molecule from the rmg_molecule. Indicies will be the same as in the RMG Molecule """ RDMol = self.rmg_molecule.toRDKitMol(removeHs=False) rdkit.Chem.AllChem.EmbedMolecule(RDMol) self.rdkit_molecule = RDMol def get_ase_molecule(self): """ A method to create an ASE Molecule from the rdkit_molecule. Indicies will be the same as in the RMG and RDKit Molecule. """ mol_list = AllChem.MolToMolBlock(self.rdkit_molecule).split('\n') ase_atoms = [] for i, line in enumerate(mol_list): if i > 3: try: atom0, atom1, bond, rest = line atom0 = int(atom0) atom0 = int(atom1) bond = float(bond) except ValueError: try: x, y, z, symbol = line.split()[0:4] x = float(x) y = float(y) z = float(z) ase_atoms.append( Atom(symbol=symbol, position=(x, y, z))) except: continue self.ase_molecule = Atoms(ase_atoms) return self.ase_molecule def view_mol(self): """ A method designed to create a 3D figure of the AutoTST_Molecule with py3Dmol from the rdkit_molecule """ mb = Chem.MolToMolBlock(self.rdkit_molecule) p = py3Dmol.view(width=400, height=400) p.addModel(mb, "sdf") p.setStyle({'stick': {}}) p.setBackgroundColor('0xeeeeee') p.zoomTo() return p.show() ############################################################################# def get_bonds(self): rdmol_copy = self.rdkit_molecule bond_list = [] for bond in rdmol_copy.GetBonds(): bond_list.append((bond.GetBeginAtomIdx(), bond.GetEndAtomIdx())) bonds = [] for indices in bond_list: i, j = indices length = self.ase_molecule.get_distance(i, j) reaction_center = "No" bond = Bond(indices=indices, length=length, reaction_center=reaction_center) bonds.append(bond) self.bonds = bonds return self.bonds def get_angles(self): rdmol_copy = self.rdkit_molecule angle_list = [] for atom1 in rdmol_copy.GetAtoms(): for atom2 in atom1.GetNeighbors(): for atom3 in atom2.GetNeighbors(): if atom1.GetIdx() == atom3.GetIdx(): continue to_add = (atom1.GetIdx(), atom2.GetIdx(), atom3.GetIdx()) if (to_add in angle_list) or (tuple(reversed(to_add)) in angle_list): continue angle_list.append(to_add) angles = [] for indices in angle_list: i, j, k = indices degree = self.ase_molecule.get_angle(i, j, k) ang = Angle(indices=indices, degree=degree, left_mask=[], right_mask=[]) left_mask = self.get_left_mask(ang) right_mask = self.get_right_mask(ang) reaction_center = "No" angles.append( Angle(indices, degree, left_mask, right_mask, reaction_center)) self.angles = angles return self.angles def get_torsions(self): rdmol_copy = self.rdkit_molecule torsion_list = [] cistrans_list = [] for bond1 in rdmol_copy.GetBonds(): atom1 = bond1.GetBeginAtom() atom2 = bond1.GetEndAtom() if atom1.IsInRing() or atom2.IsInRing(): # Making sure that bond1 we're looking at are not in a ring continue bond_list1 = list(atom1.GetBonds()) bond_list2 = list(atom2.GetBonds()) if not len(bond_list1) > 1 and not len(bond_list2) > 1: # Making sure that there are more than one bond attached to # the atoms we're looking at continue # Getting the 0th and 3rd atom and insuring that atoms # attached to the 1st and 2nd atom are not terminal hydrogens # We also make sure that all of the atoms are properly bound together # If the above are satisfied, we append a tuple of the torsion our torsion_list got_atom0 = False got_atom3 = False for bond0 in bond_list1: atomX = bond0.GetOtherAtom(atom1) # if atomX.GetAtomicNum() == 1 and len(atomX.GetBonds()) == 1: # This means that we have a terminal hydrogen, skip this # NOTE: for H_abstraction TSs, a non teminal H should exist # continue if atomX.GetIdx() != atom2.GetIdx(): got_atom0 = True atom0 = atomX for bond2 in bond_list2: atomY = bond2.GetOtherAtom(atom2) # if atomY.GetAtomicNum() == 1 and len(atomY.GetBonds()) == 1: # This means that we have a terminal hydrogen, skip this # continue if atomY.GetIdx() != atom1.GetIdx(): got_atom3 = True atom3 = atomY if not (got_atom0 and got_atom3): # Making sure atom0 and atom3 were not found continue # Looking to make sure that all of the atoms are properly bonded to eached if ("SINGLE" in str( rdmol_copy.GetBondBetweenAtoms( atom1.GetIdx(), atom2.GetIdx()).GetBondType()) and rdmol_copy.GetBondBetweenAtoms(atom0.GetIdx(), atom1.GetIdx()) and rdmol_copy.GetBondBetweenAtoms(atom1.GetIdx(), atom2.GetIdx()) and rdmol_copy.GetBondBetweenAtoms(atom2.GetIdx(), atom3.GetIdx())): torsion_tup = (atom0.GetIdx(), atom1.GetIdx(), atom2.GetIdx(), atom3.GetIdx()) already_in_list = False for torsion_entry in torsion_list: a, b, c, d = torsion_entry e, f, g, h = torsion_tup if (b, c) == (f, g) or (b, c) == (g, f): already_in_list = True if not already_in_list: torsion_list.append(torsion_tup) if ("DOUBLE" in str( rdmol_copy.GetBondBetweenAtoms( atom1.GetIdx(), atom2.GetIdx()).GetBondType()) and rdmol_copy.GetBondBetweenAtoms(atom0.GetIdx(), atom1.GetIdx()) and rdmol_copy.GetBondBetweenAtoms(atom1.GetIdx(), atom2.GetIdx()) and rdmol_copy.GetBondBetweenAtoms(atom2.GetIdx(), atom3.GetIdx())): torsion_tup = (atom0.GetIdx(), atom1.GetIdx(), atom2.GetIdx(), atom3.GetIdx()) already_in_list = False for torsion_entry in torsion_list: a, b, c, d = torsion_entry e, f, g, h = torsion_tup if (b, c) == (f, g) or (b, c) == (g, f): already_in_list = True if not already_in_list: cistrans_list.append(torsion_tup) torsions = [] cistrans = [] for indices in torsion_list: i, j, k, l = indices dihedral = self.ase_molecule.get_dihedral(i, j, k, l) tor = Torsion(indices=indices, dihedral=dihedral, left_mask=[], right_mask=[]) left_mask = self.get_left_mask(tor) right_mask = self.get_right_mask(tor) reaction_center = "No" torsions.append( Torsion(indices, dihedral, left_mask, right_mask, reaction_center)) for indices in cistrans_list: i, j, k, l = indices dihedral = self.ase_molecule.get_dihedral(i, j, k, l) tor = CisTrans(indices=indices, dihedral=dihedral, left_mask=[], right_mask=[]) left_mask = self.get_left_mask(tor) right_mask = self.get_right_mask(tor) reaction_center = "No" cistrans.append( CisTrans(indices, dihedral, left_mask, right_mask, reaction_center)) self.torsions = torsions self.cistrans = cistrans return self.torsions def get_right_mask(self, torsion_or_angle): rdmol_copy = self.rdkit_molecule rdkit_atoms = rdmol_copy.GetAtoms() if (isinstance(torsion_or_angle, autotst.geometry.Torsion) or isinstance(torsion_or_angle, autotst.geometry.CisTrans)): L1, L0, R0, R1 = torsion_or_angle.indices # trying to get the left hand side of this torsion LHS_atoms_index = [L0, L1] RHS_atoms_index = [R0, R1] elif isinstance(torsion_or_angle, autotst.geometry.Angle): a1, a2, a3 = torsion_or_angle.indices LHS_atoms_index = [a2, a1] RHS_atoms_index = [a2, a3] complete_RHS = False i = 0 atom_index = RHS_atoms_index[0] while complete_RHS is False: try: RHS_atom = rdkit_atoms[atom_index] for neighbor in RHS_atom.GetNeighbors(): if (neighbor.GetIdx() in RHS_atoms_index) or (neighbor.GetIdx() in LHS_atoms_index): continue else: RHS_atoms_index.append(neighbor.GetIdx()) i += 1 atom_index = RHS_atoms_index[i] except IndexError: complete_RHS = True right_mask = [ index in RHS_atoms_index for index in range(len(self.ase_molecule)) ] return right_mask def get_left_mask(self, torsion_or_angle): rdmol_copy = self.rdkit_molecule rdkit_atoms = rdmol_copy.GetAtoms() if (isinstance(torsion_or_angle, autotst.geometry.Torsion) or isinstance(torsion_or_angle, autotst.geometry.CisTrans)): L1, L0, R0, R1 = torsion_or_angle.indices # trying to get the left hand side of this torsion LHS_atoms_index = [L0, L1] RHS_atoms_index = [R0, R1] elif isinstance(torsion_or_angle, autotst.geometry.Angle): a1, a2, a3 = torsion_or_angle.indices LHS_atoms_index = [a2, a1] RHS_atoms_index = [a2, a3] complete_LHS = False i = 0 atom_index = LHS_atoms_index[0] while complete_LHS is False: try: LHS_atom = rdkit_atoms[atom_index] for neighbor in LHS_atom.GetNeighbors(): if (neighbor.GetIdx() in LHS_atoms_index) or (neighbor.GetIdx() in RHS_atoms_index): continue else: LHS_atoms_index.append(neighbor.GetIdx()) i += 1 atom_index = LHS_atoms_index[i] except IndexError: complete_LHS = True left_mask = [ index in LHS_atoms_index for index in range(len(self.ase_molecule)) ] return left_mask def set_rmg_coords(self, molecule_base): if molecule_base == "RDKit": mol_list = AllChem.MolToMolBlock(self.rdkit_molecule).split('\n') for i, atom in enumerate(self.rmg_molecule.atoms): j = i + 4 coords = mol_list[j].split()[:3] for k, coord in enumerate(coords): coords[k] = float(coord) atom.coords = np.array(coords) elif molecule_base == "ASE": for i, position in enumerate(self.ase_molecule.get_positions()): self.rmg_molecule.atoms[i].coords = position def update_from_rdkit_mol(self): # In order to update the ase molecule you simply need to rerun the get_ase_molecule method self.get_ase_molecule() self.set_rmg_coords("RDKit") # Getting the new torsion angles self.get_torsions() def update_from_ase_mol(self): self.set_rmg_coords("ASE") # setting the geometries of the rdkit molecule positions = self.ase_molecule.get_positions() conf = self.rdkit_molecule.GetConformers()[0] for i, atom in enumerate(self.rdkit_molecule.GetAtoms()): conf.SetAtomPosition(i, positions[i]) # Getting the new torsion angles self.get_torsions() def update_from_rmg_mol(self): conf = self.rdkit_molecule.GetConformers()[0] ase_atoms = [] for i, atom in enumerate(self.rmg_molecule.atoms): x, y, z = atom.coords symbol = atom.symbol conf.SetAtomPosition(i, [x, y, z]) ase_atoms.append(Atom(symbol=symbol, position=(x, y, z))) self.ase_molecule = Atoms(ase_atoms) # Getting the new torsion angles self.get_torsions()
class Conformer(): """ A class for generating and editing 3D conformers of molecules """ def __init__(self, smiles=None, rmg_molecule=None, index=0): self.energy = None self.index = index if (smiles or rmg_molecule): if smiles and rmg_molecule: assert rmg_molecule.isIsomorphic(RMGMolecule( SMILES=smiles)), "SMILES string did not match RMG Molecule object" self.smiles = smiles self.rmg_molecule = rmg_molecule elif rmg_molecule: self.rmg_molecule = rmg_molecule self.smiles = rmg_molecule.toSMILES() else: self.smiles = smiles self.rmg_molecule = RMGMolecule(SMILES=smiles) self.rmg_molecule.updateMultiplicity() self.get_molecules() self.get_geometries() self._symmetry_number = None else: self.smiles = None self.rmg_molecule = None self.rdkit_molecule = None self.ase_molecule = None self.bonds = [] self.angles = [] self.torsions = [] self.cistrans = [] self.chiral_centers = [] self._symmetry_number = None def __repr__(self): return '<Conformer "{}">'.format(self.smiles) def copy(self): copy_conf = Conformer() copy_conf.smiles = self.smiles copy_conf.rmg_molecule = self.rmg_molecule.copy() copy_conf.rdkit_molecule = self.rdkit_molecule.__copy__() copy_conf.ase_molecule = self.ase_molecule.copy() copy_conf.get_geometries() copy_conf.energy = self.energy return copy_conf @property def symmetry_number(self): if not self._symmetry_number: self._symmetry_number = self.calculate_symmetry_number() return self._symmetry_number def get_rdkit_mol(self): """ A method for creating an rdkit geometry from an rmg mol """ assert self.rmg_molecule, "Cannot create an RDKit geometry without an RMG molecule object" RDMol = self.rmg_molecule.toRDKitMol(removeHs=False) rdkit.Chem.AllChem.EmbedMolecule(RDMol) self.rdkit_molecule = RDMol mol_list = AllChem.MolToMolBlock(self.rdkit_molecule).split('\n') for i, atom in enumerate(self.rmg_molecule.atoms): j = i + 4 coords = mol_list[j].split()[:3] for k, coord in enumerate(coords): coords[k] = float(coord) atom.coords = np.array(coords) return self.rdkit_molecule def get_ase_mol(self): """ A method for creating an ase atoms object from an rdkit mol """ if not self.rdkit_molecule: self.get_rdkit_mol() mol_list = AllChem.MolToMolBlock(self.rdkit_molecule).split('\n') ase_atoms = [] for i, line in enumerate(mol_list): if i > 3: try: atom0, atom1, bond, rest = line atom0 = int(atom0) atom0 = int(atom1) bond = float(bond) except ValueError: try: x, y, z, symbol = line.split()[0:4] x = float(x) y = float(y) z = float(z) ase_atoms.append( Atom(symbol=symbol, position=(x, y, z))) except BaseException: continue self.ase_molecule = Atoms(ase_atoms) return self.ase_molecule def get_molecules(self): if not self.rmg_molecule: self.rmg_molecule = RMGMolecule(SMILES=self.smiles) self.rdkit_molecule = self.get_rdkit_mol() self.ase_molecule = self.get_ase_mol() self.get_geometries() return self.rdkit_molecule, self.ase_molecule def view(self): """ A method designed to create a 3D figure of the AutoTST_Molecule with py3Dmol from the rdkit_molecule """ mb = Chem.MolToMolBlock(self.rdkit_molecule) p = py3Dmol.view(width=600, height=600) p.addModel(mb, "sdf") p.setStyle({'stick': {}}) p.setBackgroundColor('0xeeeeee') p.zoomTo() return p.show() def get_bonds(self): """ A method for identifying all of the bonds in a conformer """ bond_list = [] for bond in self.rdkit_molecule.GetBonds(): bond_list.append((bond.GetBeginAtomIdx(), bond.GetEndAtomIdx())) bonds = [] for index, indices in enumerate(bond_list): i, j = indices length = self.ase_molecule.get_distance(i, j) center = False if ((self.rmg_molecule.atoms[i].label) and ( self.rmg_molecule.atoms[j].label)): center = True bond = Bond(index=index, atom_indices=indices, length=length, reaction_center=center) mask = self.get_mask(bond) bond.mask = mask bonds.append(bond) self.bonds = bonds return self.bonds def get_angles(self): """ A method for identifying all of the angles in a conformer """ angle_list = [] for atom1 in self.rdkit_molecule.GetAtoms(): for atom2 in atom1.GetNeighbors(): for atom3 in atom2.GetNeighbors(): if atom1.GetIdx() == atom3.GetIdx(): continue to_add = (atom1.GetIdx(), atom2.GetIdx(), atom3.GetIdx()) if (to_add in angle_list) or ( tuple(reversed(to_add)) in angle_list): continue angle_list.append(to_add) angles = [] for index, indices in enumerate(angle_list): i, j, k = indices degree = self.ase_molecule.get_angle(i, j, k) ang = Angle(index=index, atom_indices=indices, degree=degree, mask=[]) mask = self.get_mask(ang) reaction_center = False angles.append(Angle(index=index, atom_indices=indices, degree=degree, mask=mask, reaction_center=reaction_center)) self.angles = angles return self.angles def get_torsions(self): """ A method for identifying all of the torsions in a conformer """ torsion_list = [] for bond1 in self.rdkit_molecule.GetBonds(): atom1 = bond1.GetBeginAtom() atom2 = bond1.GetEndAtom() if atom1.IsInRing() or atom2.IsInRing(): # Making sure that bond1 we're looking at are not in a ring continue bond_list1 = list(atom1.GetBonds()) bond_list2 = list(atom2.GetBonds()) if not len(bond_list1) > 1 and not len(bond_list2) > 1: # Making sure that there are more than one bond attached to # the atoms we're looking at continue # Getting the 0th and 3rd atom and insuring that atoms # attached to the 1st and 2nd atom are not terminal hydrogens # We also make sure that all of the atoms are properly bound # together # If the above are satisfied, we append a tuple of the torsion our # torsion_list got_atom0 = False got_atom3 = False for bond0 in bond_list1: atomX = bond0.GetOtherAtom(atom1) # if atomX.GetAtomicNum() == 1 and len(atomX.GetBonds()) == 1: # This means that we have a terminal hydrogen, skip this # NOTE: for H_abstraction TSs, a non teminal H should exist # continue if atomX.GetIdx() != atom2.GetIdx(): got_atom0 = True atom0 = atomX for bond2 in bond_list2: atomY = bond2.GetOtherAtom(atom2) # if atomY.GetAtomicNum() == 1 and len(atomY.GetBonds()) == 1: # This means that we have a terminal hydrogen, skip this # continue if atomY.GetIdx() != atom1.GetIdx(): got_atom3 = True atom3 = atomY if not (got_atom0 and got_atom3): # Making sure atom0 and atom3 were not found continue # Looking to make sure that all of the atoms are properly bonded to # eached if ( "SINGLE" in str( self.rdkit_molecule.GetBondBetweenAtoms( atom1.GetIdx(), atom2.GetIdx()).GetBondType()) and self.rdkit_molecule.GetBondBetweenAtoms( atom0.GetIdx(), atom1.GetIdx()) and self.rdkit_molecule.GetBondBetweenAtoms( atom1.GetIdx(), atom2.GetIdx()) and self.rdkit_molecule.GetBondBetweenAtoms( atom2.GetIdx(), atom3.GetIdx())): torsion_tup = (atom0.GetIdx(), atom1.GetIdx(), atom2.GetIdx(), atom3.GetIdx()) already_in_list = False for torsion_entry in torsion_list: a, b, c, d = torsion_entry e, f, g, h = torsion_tup if (b, c) == (f, g) or (b, c) == (g, f): already_in_list = True if not already_in_list: torsion_list.append(torsion_tup) torsions = [] for index, indices in enumerate(torsion_list): i, j, k, l = indices dihedral = self.ase_molecule.get_dihedral(i, j, k, l) tor = Torsion(index=index, atom_indices=indices, dihedral=dihedral, mask=[]) mask = self.get_mask(tor) reaction_center = False torsions.append(Torsion(index=index, atom_indices=indices, dihedral=dihedral, mask=mask, reaction_center=reaction_center)) self.torsions = torsions return self.torsions def get_cistrans(self): """ A method for identifying all possible cistrans bonds in a molecule """ torsion_list = [] cistrans_list = [] for bond1 in self.rdkit_molecule.GetBonds(): atom1 = bond1.GetBeginAtom() atom2 = bond1.GetEndAtom() if atom1.IsInRing() or atom2.IsInRing(): # Making sure that bond1 we're looking at are not in a ring continue bond_list1 = list(atom1.GetBonds()) bond_list2 = list(atom2.GetBonds()) if not len(bond_list1) > 1 and not len(bond_list2) > 1: # Making sure that there are more than one bond attached to # the atoms we're looking at continue # Getting the 0th and 3rd atom and insuring that atoms # attached to the 1st and 2nd atom are not terminal hydrogens # We also make sure that all of the atoms are properly bound # together # If the above are satisfied, we append a tuple of the torsion our # torsion_list got_atom0 = False got_atom3 = False for bond0 in bond_list1: atomX = bond0.GetOtherAtom(atom1) # if atomX.GetAtomicNum() == 1 and len(atomX.GetBonds()) == 1: # This means that we have a terminal hydrogen, skip this # NOTE: for H_abstraction TSs, a non teminal H should exist # continue if atomX.GetIdx() != atom2.GetIdx(): got_atom0 = True atom0 = atomX for bond2 in bond_list2: atomY = bond2.GetOtherAtom(atom2) # if atomY.GetAtomicNum() == 1 and len(atomY.GetBonds()) == 1: # This means that we have a terminal hydrogen, skip this # continue if atomY.GetIdx() != atom1.GetIdx(): got_atom3 = True atom3 = atomY if not (got_atom0 and got_atom3): # Making sure atom0 and atom3 were not found continue # Looking to make sure that all of the atoms are properly bonded to # eached if ( "DOUBLE" in str( self.rdkit_molecule.GetBondBetweenAtoms( atom1.GetIdx(), atom2.GetIdx()).GetBondType()) and self.rdkit_molecule.GetBondBetweenAtoms( atom0.GetIdx(), atom1.GetIdx()) and self.rdkit_molecule.GetBondBetweenAtoms( atom1.GetIdx(), atom2.GetIdx()) and self.rdkit_molecule.GetBondBetweenAtoms( atom2.GetIdx(), atom3.GetIdx())): torsion_tup = (atom0.GetIdx(), atom1.GetIdx(), atom2.GetIdx(), atom3.GetIdx()) already_in_list = False for torsion_entry in torsion_list: a, b, c, d = torsion_entry e, f, g, h = torsion_tup if (b, c) == (f, g) or (b, c) == (g, f): already_in_list = True if not already_in_list: cistrans_list.append(torsion_tup) cistrans = [] for ct_index, indices in enumerate(cistrans_list): i, j, k, l = indices b0 = self.rdkit_molecule.GetBondBetweenAtoms(i, j) b1 = self.rdkit_molecule.GetBondBetweenAtoms(j, k) b2 = self.rdkit_molecule.GetBondBetweenAtoms(k, l) b0.SetBondDir(Chem.BondDir.ENDUPRIGHT) b2.SetBondDir(Chem.BondDir.ENDDOWNRIGHT) Chem.AssignStereochemistry(self.rdkit_molecule, force=True) if "STEREOZ" in str(b1.GetStereo()): if round(self.ase_molecule.get_dihedral(i, j, k, l), -1) == 0: atom = self.rdkit_molecule.GetAtomWithIdx(k) bonds = atom.GetBonds() for bond in bonds: indexes = [ bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()] if not ((sorted([j, k]) == sorted(indexes)) or ( sorted([k, l]) == sorted(indexes))): break for index in indexes: if not (index in indices): l = index break indices = [i, j, k, l] stero = "Z" else: if round( self.ase_molecule.get_dihedral( i, j, k, l), -1) == 180: atom = self.rdkit_molecule.GetAtomWithIdx(k) bonds = atom.GetBonds() for bond in bonds: indexes = [ bond.GetBeginAtomIdx(), bond.GetEndAtomIdx()] if not ((sorted([j, k]) == sorted(indexes)) or ( sorted([k, l]) == sorted(indexes))): break for index in indexes: if not (index in indices): l = index break indices = [i, j, k, l] stero = "E" dihedral = self.ase_molecule.get_dihedral(i, j, k, l) tor = CisTrans(index=ct_index, atom_indices=indices, dihedral=dihedral, mask=[], stero=stero) mask = self.get_mask(tor) reaction_center = False cistrans.append(CisTrans(index=ct_index, atom_indices=indices, dihedral=dihedral, mask=mask, stero=stero ) ) self.cistrans = cistrans return self.cistrans def get_mask(self, geometry): """ Getting the right hand mask for a geometry object: - self: an AutoTST Conformer object - geometry: a Bond, Angle, Dihedral, or Torsion object """ rdkit_atoms = self.rdkit_molecule.GetAtoms() if (isinstance(geometry, autotst.geometry.Torsion) or isinstance(geometry, autotst.geometry.CisTrans)): L1, L0, R0, R1 = geometry.atom_indices # trying to get the left hand side of this torsion LHS_atoms_index = [L0, L1] RHS_atoms_index = [R0, R1] elif isinstance(geometry, autotst.geometry.Angle): a1, a2, a3 = geometry.atom_indices LHS_atoms_index = [a2, a1] RHS_atoms_index = [a2, a3] elif isinstance(geometry, autotst.geometry.Bond): a1, a2 = geometry.atom_indices LHS_atoms_index = [a1] RHS_atoms_index = [a2] complete_RHS = False i = 0 atom_index = RHS_atoms_index[0] while complete_RHS is False: try: RHS_atom = rdkit_atoms[atom_index] for neighbor in RHS_atom.GetNeighbors(): if (neighbor.GetIdx() in RHS_atoms_index) or ( neighbor.GetIdx() in LHS_atoms_index): continue else: RHS_atoms_index.append(neighbor.GetIdx()) i += 1 atom_index = RHS_atoms_index[i] except IndexError: complete_RHS = True mask = [index in RHS_atoms_index for index in range( len(self.ase_molecule))] return mask def get_chiral_centers(self): """ A method to identify """ centers = rdkit.Chem.FindMolChiralCenters( self.rdkit_molecule, includeUnassigned=True) chiral_centers = [] for index, center in enumerate(centers): atom_index, chirality = center chiral_centers.append( ChiralCenter( index=index, atom_index=atom_index, chirality=chirality)) self.chiral_centers = chiral_centers return self.chiral_centers def get_geometries(self): """ A helper method to obtain all geometry things """ self.bonds = self.get_bonds() self.angles = self.get_angles() self.torsions = self.get_torsions() self.cistrans = self.get_cistrans() self.chiral_centers = self.get_chiral_centers() return ( self.bonds, self.angles, self.torsions, self.cistrans, self.chiral_centers) def update_coords(self): """ A function that creates distance matricies for the RMG, ASE, and RDKit molecules and finds which (if any) are different. If one is different, this will update the coordinates of the other two with the different one. If all three are different, nothing will happen. If all are the same, nothing will happen. """ rdkit_dm = rdkit.Chem.rdmolops.Get3DDistanceMatrix(self.rdkit_molecule) ase_dm = self.ase_molecule.get_all_distances() l = len(self.rmg_molecule.atoms) rmg_dm = np.zeros((l, l)) for i, atom_i in enumerate(self.rmg_molecule.atoms): for j, atom_j in enumerate(self.rmg_molecule.atoms): rmg_dm[i][j] = np.linalg.norm(atom_i.coords - atom_j.coords) d1 = round(abs(rdkit_dm - ase_dm).max(), 3) d2 = round(abs(rdkit_dm - rmg_dm).max(), 3) d3 = round(abs(ase_dm - rmg_dm).max(), 3) if np.all(np.array([d1, d2, d3]) > 0): return False, None if np.any(np.array([d1, d2, d3]) > 0): if d1 == 0: diff = "rmg" self.update_coords_from("rmg") elif d2 == 0: diff = "ase" self.update_coords_from("ase") else: diff = "rdkit" self.update_coords_from("rdkit") return True, diff else: return True, None def update_coords_from(self, mol_type="ase"): """ A method to update the coordinates of the RMG, RDKit, and ASE objects with a chosen object. """ possible_mol_types = ["ase", "rmg", "rdkit"] assert (mol_type.lower() in possible_mol_types), "Please specifiy a valid mol type. Valid types are {}".format( possible_mol_types) if mol_type.lower() == "rmg": conf = self.rdkit_molecule.GetConformers()[0] ase_atoms = [] for i, atom in enumerate(self.rmg_molecule.atoms): x, y, z = atom.coords symbol = atom.symbol conf.SetAtomPosition(i, [x, y, z]) ase_atoms.append(Atom(symbol=symbol, position=(x, y, z))) self.ase_molecule = Atoms(ase_atoms) # self.calculate_symmetry_number() elif mol_type.lower() == "ase": conf = self.rdkit_molecule.GetConformers()[0] for i, position in enumerate(self.ase_molecule.get_positions()): self.rmg_molecule.atoms[i].coords = position conf.SetAtomPosition(i, position) # self.calculate_symmetry_number() elif mol_type.lower() == "rdkit": mol_list = AllChem.MolToMolBlock(self.rdkit_molecule).split('\n') for i, atom in enumerate(self.rmg_molecule.atoms): j = i + 4 coords = mol_list[j].split()[:3] for k, coord in enumerate(coords): coords[k] = float(coord) atom.coords = np.array(coords) self.get_ase_mol() # self.calculate_symmetry_number() def set_bond_length(self, bond_index, length): """ This is a method to set bond lengths Variabels: - bond_index (int): the index of the bond you want to edit - length (float, int): the distance you want to set the bond (in angstroms) """ assert isinstance(length, (float, int)) matched = False for bond in self.bonds: if bond.index == bond_index: matched = True break if not matched: logging.info("Angle index provided is out of range. Nothing was changed.") return self i, j = bond.atom_indices self.ase_molecule.set_distance( a0=i, a1=j, distance=length, mask=bond.mask, fix=0 ) bond.length = length self.update_coords_from(mol_type="ase") return self def set_angle(self, angle_index, angle): """ A method that will set the angle of an Angle object accordingly """ assert isinstance( angle, (int, float)), "Plese provide a float or an int for the angle" matched = False for a in self.angles: if a.index == angle_index: matched = True break if not matched: logging.info("Angle index provided is out of range. Nothing was changed.") return self i, j, k = a.atom_indices self.ase_molecule.set_angle( a1=i, a2=j, a3=k, angle=angle, mask=a.mask ) a.degree = angle self.update_coords_from(mol_type="ase") return self def set_torsion(self, torsion_index, dihedral): """ A method that will set the diehdral angle of a Torsion object accordingly. """ assert isinstance( dihedral, (int, float)), "Plese provide a float or an int for the diehdral angle" matched = False for torsion in self.torsions: if torsion.index == torsion_index: matched = True break if not matched: logging.info("Torsion index provided is out of range. Nothing was changed.") return self i, j, k, l = torsion.atom_indices self.ase_molecule.set_dihedral( a1=i, a2=j, a3=k, a4=l, angle=dihedral, mask=torsion.mask ) torsion.dihedral = dihedral self.update_coords_from(mol_type="ase") return self def set_cistrans(self, cistrans_index, stero="E"): """ A module that will set a corresponding cistrans bond to the proper E/Z config """ assert stero.upper() in [ "E", "Z"], "Please specify a valid stero direction." matched = False for cistrans in self.cistrans: if cistrans.index == cistrans_index: matched = True break if not matched: logging.info("CisTrans index provided is out of range. Nothing was changed.") return self if cistrans.stero == stero.upper(): self.update_coords_from("ase") return self else: cistrans.stero = stero.upper() i, j, k, l = cistrans.atom_indices self.ase_molecule.rotate_dihedral( a1=i, a2=j, a3=k, a4=l, angle=float(180), mask=cistrans.mask ) cistrans.stero = stero.upper() self.update_coords_from(mol_type="ase") return self def set_chirality(self, chiral_center_index, stero="R"): """ A module that can set the orientation of a chiral center. """ assert stero.upper() in ["R", "S"], "Specify a valid stero orientation" centers_dict = { 'R': Chem.rdchem.ChiralType.CHI_TETRAHEDRAL_CW, 'S': Chem.rdchem.ChiralType.CHI_TETRAHEDRAL_CCW } assert isinstance(chiral_center_index, int), "Please provide an integer for the index" rdmol = self.rdkit_molecule.__copy__() match = False for chiral_center in self.chiral_centers: if chiral_center.index == chiral_center_index: match = True break if not match: logging.info("ChiralCenter index provided is out of range. Nothing was changed") return self rdmol.GetAtomWithIdx(chiral_center.atom_index).SetChiralTag( centers_dict[stero.upper()]) rdkit.Chem.rdDistGeom.EmbedMolecule(rdmol) old_torsions = self.torsions[:] + self.cistrans[:] self.rdkit_molecule = rdmol self.update_coords_from(mol_type="rdkit") # Now resetting dihedral angles in case if they changed. for torsion in old_torsions: i, j, k, l = torsion.atom_indices self.ase_molecule.set_dihedral( a1=i, a2=j, a3=k, a4=l, mask=torsion.mask, angle=torsion.dihedral, ) self.update_coords_from(mol_type="ase") return self def calculate_symmetry_number(self): from rmgpy.qm.symmetry import PointGroupCalculator from rmgpy.qm.qmdata import QMData atom_numbers = self.ase_molecule.get_atomic_numbers() coordinates = self.ase_molecule.get_positions() qmdata = QMData( groundStateDegeneracy=1, # Only needed to check if valid QMData numberOfAtoms=len(atom_numbers), atomicNumbers=atom_numbers, atomCoords=(coordinates, str('angstrom')), energy=(0.0, str('kcal/mol')) # Only needed to avoid error ) settings = type(str(''), (), dict(symmetryPath=str( 'symmetry'), scratchDirectory="."))() # Creates anonymous class pgc = PointGroupCalculator(settings, self.smiles, qmdata) pg = pgc.calculate() #os.remove("{}.symm".format(self.smiles)) if pg is not None: symmetry_number = pg.symmetryNumber else: symmetry_number = 1 return symmetry_number
def lattice_alteration_nn(indiv, Optimizer): """Move function to perform random move along random axis for nearest neighbor distance to random atoms Inputs: indiv = Individual class object to be altered Optimizer = Optimizer class object with needed parameters Outputs: indiv = Altered Individual class object """ if 'MU' in Optimizer.debug: debug = True else: debug = False if Optimizer.structure=='Defect': if Optimizer.isolate_mutation: indc,indb,vacant,swaps,stro = find_defects(indiv[0],Optimizer.solidbulk,0) ind = indc.copy() ind.extend(indb) else: ind=indiv[0].copy() indc=indiv[0].copy() else: ind=indiv[0].copy() indc=indiv[0].copy() if len(indc) != 0: ctoff1 = [1.0 for one in ind] nl = NeighborList(ctoff1, bothways=True, self_interaction=False) nl.update(ind) try: natomsmove=random.randint(1,len(indc)/2) except ValueError: natomsmove=1 passn=0 for count in range(natomsmove): try: indexmv = random.choice([i for i in range(len(indc))]) indices, offsets = nl.get_neighbors(indexmv) nns = Atoms() nns.append(ind[indexmv]) for index, d in zip(indices,offsets): index = int(index) pos = ind[index].position + numpy.dot(d,ind.get_cell()) nns.append(Atom(symbol=ind[index].symbol, position=pos)) dist = [nns.get_distance(0,i) for i in range(1, len(nns))] r = sum(dist)/len(dist) dir = random.choice([[1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]]) ind[indexmv].position += [i*r for i in dir] except: passn+=1 indiv[0]=ind.copy() else: natomsmove=0 passn=0 Optimizer.output.write('Lattice Alteration NN Mutation performed on individual\n') Optimizer.output.write('Index = '+repr(indiv.index)+'\n') natomsmove-=passn Optimizer.output.write('Number of atoms moved = '+repr(natomsmove)+'\n') Optimizer.output.write(repr(indiv[0])+'\n') muttype='LANN'+repr(natomsmove) if indiv.energy==0: indiv.history_index=indiv.history_index+'m'+muttype else: indiv.history_index=repr(indiv.index)+'m'+muttype return indiv
import numpy as np from ase import Atoms from ase.optimize import BFGS from gpaw import GPAW from gpaw.wavefunctions.pw import PW from gpaw.test import equal from gpaw.mpi import world a = 2.65 slab = Atoms('Li2', [(0, 0, 0), (0, 0, a)], cell=(a, a, 3 * a), pbc=True) k = 4 calc = GPAW(mode=PW(200), eigensolver='rmm-diis', parallel={'band': min(world.size, 4)}, idiotproof=0, kpts=(k, k, 1)) slab.set_calculator(calc) BFGS(slab).run(fmax=0.01) assert abs(slab.get_distance(0, 1) - 2.4594) < 0.001, slab.get_distance(0, 1)
f_ext = 0.2 atom1 = 0 atom2 = 1 atom3 = 2 fmax = 0.001 atoms = Atoms('H3', positions=[(0, 0, 0), (0.751, 0, 0), (0, 1., 0)]) atoms.set_calculator(EMT()) # Without external force opt = FIRE(atoms) opt.run(fmax=fmax) dist1 = atoms.get_distance(atom1, atom2) # With external force con1 = ExternalForce(atom1, atom2, f_ext) atoms.set_constraint(con1) opt = FIRE(atoms) opt.run(fmax=fmax) dist2 = atoms.get_distance(atom1, atom2) # Distance should increase due to the external force assert dist2 > dist1 # Combine ExternalForce with FixBondLength # Fix the bond on which the force acts con2 = FixBondLength(atom1, atom2) # ExternalForce constraint at the beginning of the list!!!
from ase import Atoms from ase.calculators.emt import EMT from ase.constraints import FixBondLengths from ase.optimize import BFGS, QuasiNewton from ase.neb import SingleCalculatorNEB from ase.lattice.surface import fcc111, add_adsorbate from math import sqrt, cos, sin zpos = cos(134.3/2.0*pi/180.0)*1.197 xpos = sin(134.3/2.0*pi/180.0)*1.19 co2 = Atoms('COO', positions=[(-xpos+1.2,0,-zpos), (-xpos+1.2,-1.1,-zpos), (-xpos+1.2,1.1,-zpos)]) slab = fcc111('Au', size=(2, 2, 4), vacuum=2*5, orthogonal=True) slab.center() add_adsorbate(slab,co2,1.5,'bridge') slab.set_pbc((True,True,False)) d0 = co2.get_distance(-3, -2) d1 = co2.get_distance(-3, -1) calc = EMT() slab.set_calculator(calc) constraint = FixBondLengths([[-3,-2],[-3,-1]]) slab.set_constraint(constraint) dyn = BFGS(slab, trajectory='relax.traj') dyn.run(fmax=0.05) assert abs(co2.get_distance(-3, -2) - d0) < 1e-14 assert abs(co2.get_distance(-3, -1) - d1) < 1e-14
from ase import Atoms from ase.calculators.emt import EMT from ase.optimize import QuasiNewton n2 = Atoms('N2', positions=[(0, 0, 0), (0, 0, 1.1)], calculator=EMT()) QuasiNewton(n2).run(0.01) print(n2.get_distance(0, 1), n2.get_potential_energy())
from math import sqrt, pi from ase import Atoms from ase.calculators.emt import EMT from ase.constraints import FixBondLengths from ase.optimize import BFGS, QuasiNewton from ase.neb import SingleCalculatorNEB from ase.lattice.surface import fcc111, add_adsorbate from math import sqrt, cos, sin zpos = cos(134.3 / 2.0 * pi / 180.0) * 1.197 xpos = sin(134.3 / 2.0 * pi / 180.0) * 1.19 co2 = Atoms('COO', positions=[(-xpos + 1.2, 0, -zpos), (-xpos + 1.2, -1.1, -zpos), (-xpos + 1.2, 1.1, -zpos)]) slab = fcc111('Au', size=(2, 2, 4), vacuum=2 * 5, orthogonal=True) slab.center() add_adsorbate(slab, co2, 1.5, 'bridge') slab.set_pbc((True, True, False)) d0 = co2.get_distance(-3, -2) d1 = co2.get_distance(-3, -1) calc = EMT() slab.set_calculator(calc) constraint = FixBondLengths([[-3, -2], [-3, -1]]) slab.set_constraint(constraint) dyn = BFGS(slab, trajectory='relax.traj') dyn.run(fmax=0.05) assert abs(co2.get_distance(-3, -2) - d0) < 1e-14 assert abs(co2.get_distance(-3, -1) - d1) < 1e-14
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))
name = dire + 'Pt-bulk-fcc-%.0f-%.0f-%.4f' % (ecut, k, a) filename = name + '.txt' b = a / 2 c = 'a-Pt-%.0f-%.0f-%.4f' % (ecut, k, a) bulk = Atoms('Pt', cell=[[0, b, b], [b, 0, b], [b, b, 0]], pbc=True) #bulk = L1_0(symbol=('Co','Pt'),latticeconstant=(a,c), pbc=True) if not os.path.exists(os.path.dirname(filename)): try: os.makedirs(os.path.dirname(filename)) except OSError as exc: # Guard against race condition if exc.errno != errno.EEXIST: raise bulk *= [2, 2, 2] distancebefore = bulk.get_distance(1, 2) bulk.pop(4) bulk.append(Atom('Co', [0.0, 1.96, 1.96])) bulk.pop(6) bulk.append( Atom('Co', [ 3.9199999999999999, 3.9199999999999999, 3.9199999999999999 ])) #bulk.set_initial_magnetic_moments([1,1,1,1]) calc = GPAW( mode=PW(ecut), # cutoff kpts=(k, k, k), # k-points xc='PBE', txt=filename) # output file bulk.set_calculator(calc) relax = QuasiNewton(bulk, logfile=c + '.log')
F1 = np.polyval(np.polyder(np.polyfit(D, E, 7)), D) F2 = F[:, :3, 0].sum(1) error = abs(F1 - F2).max() dimer.constraints = FixBondLengths([(3 * i + j, 3 * i + (j + 1) % 3) for i in range(2) for j in [0, 1, 2]]) opt = BFGS(dimer, trajectory=calc.name + '.traj', logfile=calc.name + 'd.log') opt.run(0.001) if calc.name == 'tip4p': # save optimized geom for EIQMMM test tip4pdimer = dimer.copy() e0 = dimer.get_potential_energy() d0 = dimer.get_distance(0, 3) R = dimer.positions v1 = R[2] - R[3] v2 = R[3] - (R[4] + R[5]) / 2 a0 = np.arccos(np.dot(v1, v2) / (np.dot(v1, v1) * np.dot(v2, v2))**0.5) / np.pi * 180 fmt = '{0:>25}: {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 # plt.show() print(fmt.format('reference', 9.999, eexp, dexp, aexp))
from ase import Atoms from ase.calculators.emt import EMT from ase.constraints import FixBondLength from ase.io import Trajectory from ase.optimize import BFGS a = 3.6 b = a / 2 cu = Atoms('Cu2Ag', positions=[(0, 0, 0), (b, b, 0), (a, a, b)], calculator=EMT()) e0 = cu.get_potential_energy() print(e0) d0 = cu.get_distance(0, 1) cu.set_constraint(FixBondLength(0, 1)) t = Trajectory('cu2ag.traj', 'w', cu) qn = BFGS(cu) qn.attach(t.write) def f(): print(cu.get_distance(0,1)) qn.attach(f) qn.run(fmax=0.01) assert abs(cu.get_distance(0, 1) - d0) < 1e-14
def check_min_dist(Optimizer, totalsol, type='Defect', nat=None, min_len=0.7, STR=''): if type=='Defect' or type=='Crystal' or type=='Surface': if nat==None: nat=len(totalsol) cutoffs=[2.0 for one in totalsol] nl=NeighborList(cutoffs,bothways=True,self_interaction=False) nl.update(totalsol) for one in totalsol[0:nat]: nbatoms=Atoms() nbatoms.append(one) indices, offsets=nl.get_neighbors(one.index) for index, d in zip(indices,offsets): index = int(index) sym=totalsol[index].symbol pos=totalsol[index].position + numpy.dot(d,totalsol.get_cell()) at=Atom(symbol=sym,position=pos) nbatoms.append(at) while True: dflag=False for i in range(1,len(nbatoms)): d=nbatoms.get_distance(0,i) if d < min_len: nbatoms.set_distance(0,i,min_len+.01,fix=0.5) STR+='--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n' dflag=True if dflag==False: break for i in range(len(indices)): totalsol[indices[i]].position=nbatoms[i+1].position totalsol[one.index].position=nbatoms[0].position nl.update(totalsol) elif type=='Cluster': rank = MPI.COMM_WORLD.Get_rank() logger = logging.getLogger(Optimizer.loggername) R = totalsol.arrays['positions'] tol = 0.01 epsilon = 0.05 fix = 0.5 if Optimizer.forcing == 'EllipoidShape' or Optimizer.forcing == 'FreeNatom': com = totalsol.get_center_of_mass() cmax = numpy.maximum.reduce(R) cmin = numpy.minimum.reduce(R) rmax= (cmax-cmin)/2.0 if Optimizer.forcing == 'FreeNatom': rcutoff = 44.0 cutoff = [44.0,44.0,20.0] else: rcutoff = 11.0 cutoff = [12.0,12.0,12.0] rcutoff = 44.0 cutoff = [44.0,44.0,20.0] #check if atoms are isolated outside of cluster cutoffs=[3.0 for one in totalsol] nl=NeighborList(cutoffs,bothways=True,self_interaction=False) nl.update(totalsol) for i in range(len(totalsol)): indices, offsets=nl.get_neighbors(i) D = R[i]-com radius = (numpy.dot(D,D))**0.5 #numpy.linalg.norm(D) if len(indices) < 12 or radius > rcutoff : # logger.info('M:move atoms back when indice {0} or radius {1}'.format(len(indices),radius)) # R[i] = [com[j] + D[j]/radius*rcutoff for j in range(3)] theta=math.radians(random.uniform(0,360)) phi=math.radians(random.uniform(0,180)) R[i][0] = com[0] + (rmax[0]+2.5)*math.sin(theta)*math.cos(phi) #allow atoms expend by 2.5 ang R[i][1] = com[1] + (rmax[1]+2.5)*math.sin(theta)*math.sin(phi) R[i][2] = com[2] + rmax[2]*math.cos(theta) # logger.info('M:move atoms back new pos {0} {1} {2}'.format(rmax[0]*math.sin(theta)*math.cos(phi),rmax[1]*math.sin(theta)*math.sin(phi),rmax[2]*math.cos(theta))) # check if atoms are within cluster region for i in range(0,len(totalsol)): # D = R[i]-com for j in range(3): if D[j] > cutoff[j] or D[j] < -cutoff[j]: # logger.info('M:before move R {0} & com {1}'.format(R[i][j],com[j])) #if rank==0: # print "before:",j,R[i][j],com[j] R[i][j] = com[j]+numpy.sign(D[j])*cutoff[j]*random.random() # logger.info('M:after move R {0} '.format(R[i][j])) #if rank==0: # print "after:",R[i][j] # STR+='--- WARNING: Atoms too far along x-y (>44A) - Implement Move ---\n' D = R[i]-com # radius = (numpy.dot(D,D))**0.5 #numpy.linalg.norm(D) # STR+='--- WARNING: Atoms too far (>56A) - Implement Move ---\n' closelist = numpy.arange(len(totalsol)) iter = 0 while len(closelist) > 0 and iter<2: iter+=1 # checklist = numpy.copy(closelist) closelist = [] dist=scipy.spatial.distance.cdist(R,R) numpy.fill_diagonal(dist,1.0) smalldist = numpy.where(dist < min_len-tol) # for i in checklist: # for j in range(i+1,len(totalsol)): # if len(checklist) == len(totalsol): # jstart = i+1 # else: # jstart = 0 # for j in range(jstart,len(totalsol)): # if i != j and dist[i][j] < min_len: # d=totalsol.get_distance(i,j) # if d < min_len: # totalsol.set_distance(i,j,min_len,fix=0.5) # d = (D[0]*D[0]+D[1]*D[1]+D[2]*D[2])**0.5 for ind in range(len(smalldist[0])): i = smalldist[0][ind] j = smalldist[1][ind] if i < j and dist[i][j] < min_len-tol: closelist.append(i) closelist.append(j) if dist[i][j] > epsilon: x = 1.0 - min_len / dist[i][j] D = R[j]-R[i] # print "node:",rank,"x",x,R[i],R[j],D, dist[i][j] R[i] += (x * fix) * D R[j] -= (x * (1.0 - fix)) * D else: R[i] += [0.2, 0.0, 0.0] R[j] -= [0.2, 0.0, 0.0] R2P = [R[i],R[j]] dist2P=scipy.spatial.distance.cdist(R2P,R) dist[i] = dist2P[0] dist[j] = dist2P[1] for k in range(len(R)): dist[k][i] = dist[i][k] dist[k][j] = dist[j][k] # STR+='--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n' closelist=list(set(closelist)) closelist.sort() if len(closelist) != 0: logger.info('M:iter {0}, closelist size {1}'.format(iter,len(closelist))) # print "rank", rank, closelist else: print 'WARNING: In Check_Min_Dist in EvalEnergy: Structure Type not recognized' return totalsol, STR
from ase.calculators.emt import EMT from ase.constraints import FixBondLengths from ase.optimize import BFGS from ase.build import fcc111, add_adsorbate for wrap in [False, True]: zpos = cos(134.3 / 2.0 * pi / 180.0) * 1.197 xpos = sin(134.3 / 2.0 * pi / 180.0) * 1.19 co2 = Atoms('COO', positions=[(-xpos + 1.2, 0, -zpos), (-xpos + 1.2, -1.1, -zpos), (-xpos + 1.2, 1.1, -zpos)]) slab = fcc111('Au', size=(2, 2, 4), vacuum=2 * 5, orthogonal=True) slab.center() add_adsorbate(slab, co2, 1.5, 'bridge') slab.set_pbc((True, True, False)) d0 = co2.get_distance(-3, -2) d1 = co2.get_distance(-3, -1) calc = EMT() slab.set_calculator(calc) if wrap: # Remap into the cell so bond is actually wrapped: slab.set_scaled_positions(slab.get_scaled_positions() % 1.0) constraint = FixBondLengths([[-3, -2], [-3, -1]]) slab.set_constraint(constraint) dyn = BFGS(slab, trajectory='relax_%d.traj' % wrap) dyn.run(fmax=0.05) assert abs(slab.get_distance(-3, -2, mic=1) - d0) < 1e-9 assert abs(slab.get_distance(-3, -1, mic=1) - d1) < 1e-9
a = 10 d = 1.17103 CO2 = Atoms('CO2', positions=[[a / 2, a / 2, a / 2], [a / 2 - d, a / 2, a / 2], [a / 2 + d, a / 2, a / 2]], cell=(a, a, a)) # Next, we need to create a calculator object # The optimization calculation # First we create the GPAW object calc_PBE = GPAW(xc='PBE') CO2.set_calculator(calc_PBE) # By the way: by default, convergence of SCF is such that difference in energy is < 0.0005 # We can change this by adding the parameter: convergence={'energy': 0.0001} # Next, we use a relaxation procedure # Here, the atoms will be moved small steps in search for the optimal structure until the force on each atom is less that 0.05 eV/Ang relax = QuasiNewton(CO2) relax.run(fmax=0.05) CO2.get_distance(0, 1) # We started from the correct structure, that's why the optimization finished in only 1 step. # Change d to 1 and see what happens. The optimization will finish in a number of iterations.
from ase import Atoms from ase.calculators.emt import EMT from ase.constraints import FixBondLength from ase.io import PickleTrajectory from ase.optimize import BFGS a = 3.6 b = a / 2 cu = Atoms('Cu2Ag', positions=[(0, 0, 0), (b, b, 0), (a, a, b)], calculator=EMT()) e0 = cu.get_potential_energy() print e0 d0 = cu.get_distance(0, 1) cu.set_constraint(FixBondLength(0, 1)) t = PickleTrajectory('cu2ag.traj', 'w', cu) qn = BFGS(cu) qn.attach(t.write) def f(): print cu.get_distance(0,1) qn.attach(f) qn.run(fmax=0.01) assert abs(cu.get_distance(0, 1) - d0) < 1e-14
def lattice_alteration_nn(indiv, Optimizer): """Move function to perform random move along random axis for nearest neighbor distance to random atoms Inputs: indiv = Individual class object to be altered Optimizer = Optimizer class object with needed parameters Outputs: indiv = Altered Individual class object """ if 'MU' in Optimizer.debug: debug = True else: debug = False if Optimizer.structure == 'Defect': if Optimizer.isolate_mutation: indc, indb, vacant, swaps, stro = find_defects( indiv[0], Optimizer.solidbulk, 0) ind = indc.copy() ind.extend(indb) else: ind = indiv[0].copy() indc = indiv[0].copy() else: ind = indiv[0].copy() indc = indiv[0].copy() if len(indc) != 0: ctoff1 = [1.0 for one in ind] nl = NeighborList(ctoff1, bothways=True, self_interaction=False) nl.update(ind) try: natomsmove = random.randint(1, len(indc) / 2) except ValueError: natomsmove = 1 passn = 0 for count in range(natomsmove): try: indexmv = random.choice([i for i in range(len(indc))]) indices, offsets = nl.get_neighbors(indexmv) nns = Atoms() nns.append(ind[indexmv]) for index, d in zip(indices, offsets): index = int(index) pos = ind[index].position + numpy.dot(d, ind.get_cell()) nns.append(Atom(symbol=ind[index].symbol, position=pos)) dist = [nns.get_distance(0, i) for i in range(1, len(nns))] r = sum(dist) / len(dist) dir = random.choice([[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]]) ind[indexmv].position += [i * r for i in dir] except: passn += 1 indiv[0] = ind.copy() else: natomsmove = 0 passn = 0 Optimizer.output.write( 'Lattice Alteration NN Mutation performed on individual\n') Optimizer.output.write('Index = ' + repr(indiv.index) + '\n') natomsmove -= passn Optimizer.output.write('Number of atoms moved = ' + repr(natomsmove) + '\n') Optimizer.output.write(repr(indiv[0]) + '\n') muttype = 'LANN' + repr(natomsmove) if indiv.energy == 0: indiv.history_index = indiv.history_index + 'm' + muttype else: indiv.history_index = repr(indiv.index) + 'm' + muttype return indiv
from ase.lattice.surface import fcc111, add_adsorbate from math import sqrt, cos, sin for mic in [ False, True ]: zpos = cos(134.3/2.0*pi/180.0)*1.197 xpos = sin(134.3/2.0*pi/180.0)*1.19 co2 = Atoms('COO', positions=[(-xpos+1.2,0,-zpos), (-xpos+1.2,-1.1,-zpos), (-xpos+1.2,1.1,-zpos)]) slab = fcc111('Au', size=(2, 2, 4), vacuum=2*5, orthogonal=True) slab.center() add_adsorbate(slab,co2,1.5,'bridge') slab.set_pbc((True,True,False)) d0 = co2.get_distance(-3, -2) d1 = co2.get_distance(-3, -1) calc = EMT() slab.set_calculator(calc) if mic: # Remap into the cell so bond is actually wrapped. slab.set_scaled_positions(slab.get_scaled_positions()%1.0) constraint = FixBondLengths([[-3,-2],[-3,-1]], mic=True, atoms=slab) else: constraint = FixBondLengths([[-3,-2],[-3,-1]]) slab.set_constraint(constraint) dyn = BFGS(slab, trajectory='relax_%s.traj' % ('mic' if mic else 'no_mic')) dyn.run(fmax=0.05) assert abs(slab.get_distance(-3, -2, mic=mic) - d0) < 1e-9 assert abs(slab.get_distance(-3, -1, mic=mic) - d1) < 1e-9
# corresponding total energy, bond length and angle for e_s in e_shifts: starttime = time.time() calc = Siesta('h2o',meshcutoff=200.0 * units.Ry, mix=0.5, pulay=4) calc.set_fdf('PAO.EnergyShift', e_s * units.eV) calc.set_fdf('PAO.SplitNorm', 0.15) calc.set_fdf('PAO.BasisSize', 'SZ') h2o.set_calculator(calc) # Make a -traj file named h2o_current_shift.traj: dyn = QuasiNewton(h2o, trajectory='h2o_%s.traj' % e_s) dyn.run(fmax=0.02) # Perform the relaxation E = h2o.get_potential_energy() print # Make the output more readable print "E_shift: %.2f" %e_s print "----------------" print "Total Energy: %.4f" % E # Print total energy d = h2o.get_distance(0,2) print "Bond length: %.4f" % d # Print bond length p = h2o.positions d1 = p[0] - p[2] d2 = p[1] - p[2] r = np.dot(d1, d2) / (np.linalg.norm(d1) * np.linalg.norm(d2)) angle = np.arccos(r) / pi * 180 print "Bond angle: %.4f" % angle # Print bond angle endtime = time.time() walltime = endtime - starttime print 'Wall time: %.5f' % walltime print # Make the output more readable
co.pbc = True t.write(co) del t # append to a nonexisting file fname = '2.nc' if os.path.isfile(fname): os.remove(fname) t = NetCDFTrajectory(fname, 'a', co) del t fname = '3.nc' t = NetCDFTrajectory(fname, 'w', co) # File is not created before first write co.set_pbc([True, False, False]) d = co.get_distance(0, 1) with warnings.catch_warnings(): warnings.simplefilter('ignore', UserWarning) t.write(co) del t # Check pbc for c in [1, 1000]: t = NetCDFTrajectory(fname, chunk_size=c) a = t[-1] assert a.pbc[0] and not a.pbc[1] and not a.pbc[2] assert abs(a.get_distance(0, 1) - d) < 1e-6 del t # Append something in Voigt notation t = NetCDFTrajectory(fname, 'a') for frame, a in enumerate(t): test = np.random.random([len(a), 6])
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))
from ase import Atoms # Setup a chain of H,O,C # H-O Dist = 2 # O-C Dist = 3 # C-H Dist = 5 with mic=False # C-H Dist = 4 with mic=True a = Atoms('HOC', positions=[(1, 1, 1), (3, 1, 1), (6, 1, 1)]) a.set_cell((9, 2, 2)) a.set_pbc((True, False, False)) # Calculate indiviually with mic=True assert a.get_distance(0, 1, mic=True) == 2 assert a.get_distance(1, 2, mic=True) == 3 assert a.get_distance(0, 2, mic=True) == 4 # Calculate indiviually with mic=False assert a.get_distance(0, 1, mic=False) == 2 assert a.get_distance(1, 2, mic=False) == 3 assert a.get_distance(0, 2, mic=False) == 5 # Calculate in groups with mic=True assert (a.get_distances(0, [1, 2], mic=True) == [2, 4]).all() # Calculate in groups with mic=False assert (a.get_distances(0, [1, 2], mic=False) == [2, 5]).all() # Calculate all with mic=True assert (a.get_all_distances(mic=True) == [[0, 2, 4], [2, 0, 3],
from ase import Atom, Atoms m = Atoms('H2') a = m[0] b = Atom('H') for c in [a, b]: assert c.x == 0 c.z = 24.0 assert c.position[2] == 24.0 assert c.symbol == 'H' c.number = 92 assert c.symbol == 'U' c.symbol = 'Fe' assert c.number == 26 c.tag = 42 assert c.tag == 42 c.momentum = (1,2,3) assert m[0].tag == 42 momenta = m.get_momenta() m = Atoms('LiH') for a in m: print(a.symbol) for a in m: if a.symbol == 'H': a.z = 0.75 assert m.get_distance(0, 1) == 0.75 a = m.pop() m += a del m[:1] print(m)
t.write(co) del t # append to a nonexisting file if netcdftrajectory.have_nc == netcdftrajectory.NC_IS_NETCDF4: fname = '2.nc' if os.path.isfile(fname): os.remove(fname) t = NetCDFTrajectory(fname, 'a', co) del t fname = '3.nc' t = NetCDFTrajectory(fname, 'w', co) # File is not created before first write co.set_pbc([True, False, False]) d = co.get_distance(0, 1) t.write(co) del t # Check pbc t = NetCDFTrajectory(fname) a = t[-1] assert a.pbc[0] and not a.pbc[1] and not a.pbc[2] assert abs(a.get_distance(0, 1) - d) < 1e-6 del t # Append something in Voigt notation t = NetCDFTrajectory(fname, 'a') for frame, a in enumerate(t): test = np.random.random([len(a), 6]) a.set_array('test', test) t.write_arrays(a, frame, ['test']) del t
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))
# 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 = 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)) assert abs(e0 + eexp) < 0.002 assert abs(d0 - dexp) < 0.006 assert abs(a0 - aexp) < 2 print(fmt.format('reference', 9.999, eexp, dexp, aexp)) # plt.show()
mode=PW(700), kpts=kpts, xc='PBE', txt=myGpaw.symstr+'.out', occupations=FermiDirac(width=0.05), # nbands=-2, convergence={'energy': 0.0005, # eV / electron 'density': 1.0e-4, 'eigenstates': 4.0e-8, # eV^2 / electron 'bands': 'CBM+2.0', 'forces': float('inf')}, # eV / Ang Max ) atoms.calc = calc e2 = atoms.get_potential_energy() d0 = atoms.get_distance(0, 1) calc.write(myGpaw.symstr+'.gpw') fd = open('optimization.txt', 'w') print('experimental bond length:', file=fd) print('experimental energy: %5.2f eV' % e2, file=fd) print('bondlength : %5.2f Ang' % d0, file=fd) # # Find the theoretical bond length: relax = QuasiNewton(atoms, logfile='qn.log', trajectory=myGpaw.symstr+'.emt.traj') relax.run(fmax=0.05) e2 = atoms.get_potential_energy() d0 = atoms.get_distance(0, 1) # calc.write(myGpaw.symstr+'relax.gpw')
dimer.positions[3:, 0] += 2.8 dimer.constraints = FixBondLengths([ ((selection[i] + 3) % 6, (selection[i - 1] + 3) % 6) for i in range(3) ]) dimer.calc = EIQMMM(selection, GPAW(txt=name + '.txt', h=0.16), TIP4P(), interaction, vacuum=4, embedding=Embedding(rc=0.2, rc2=20, width=1), output=name + '.out') opt = LBFGS(dimer, trajectory=name + '.traj') opt.run(0.02) monomer = dimer[selection] monomer.center(vacuum=4) monomer.calc = GPAW(txt=name + 'M.txt', h=0.16) opt = PreconLBFGS(monomer, precon=Exp(A=3), trajectory=name + 'M.traj') opt.run(0.02) e0 = monomer.get_potential_energy() be = dimer.get_potential_energy() - e0 d = dimer.get_distance(0, 3) print(name, be, d) if name == '012': assert abs(be - -0.288) < 0.002 assert abs(d - 2.76) < 0.02 else: assert abs(be - -0.316) < 0.002 assert abs(d - 2.67) < 0.02
def test_emt1(testdir): a = 3.6 b = a / 2 cu = Atoms('Cu2Ag', positions=[(0, 0, 0), (b, b, 0), (a, a, b)], calculator=EMT()) e0 = cu.get_potential_energy() print(e0) d0 = cu.get_distance(0, 1) cu.set_constraint(FixBondLength(0, 1)) def f(): print(cu.get_distance(0, 1)) qn = BFGS(cu) with Trajectory('cu2ag.traj', 'w', cu) as t: qn.attach(t.write) qn.attach(f) qn.run(fmax=0.001) assert abs(cu.get_distance(0, 1) - d0) < 1e-14
def test_CO2_Au111(wrap, testdir): zpos = cos(134.3 / 2.0 * pi / 180.0) * 1.197 xpos = sin(134.3 / 2.0 * pi / 180.0) * 1.19 co2 = Atoms('COO', positions=[(-xpos + 1.2, 0, -zpos), (-xpos + 1.2, -1.1, -zpos), (-xpos + 1.2, 1.1, -zpos)]) slab = fcc111('Au', size=(2, 2, 4), vacuum=2 * 5, orthogonal=True) slab.center() add_adsorbate(slab, co2, 1.5, 'bridge') slab.set_pbc((True, True, False)) d0 = co2.get_distance(-3, -2) d1 = co2.get_distance(-3, -1) calc = EMT() slab.calc = calc if wrap: # Remap into the cell so bond is actually wrapped: slab.set_scaled_positions(slab.get_scaled_positions() % 1.0) constraint = FixBondLengths([[-3, -2], [-3, -1]]) slab.set_constraint(constraint) with BFGS(slab, trajectory='relax_%d.traj' % wrap) as dyn: dyn.run(fmax=0.05) assert abs(slab.get_distance(-3, -2, mic=1) - d0) < 1e-9 assert abs(slab.get_distance(-3, -1, mic=1) - d1) < 1e-9
class Polycrystal(OrthoBox): def __init__(self, boxsize, numcrystallites, crystallitenames, crystallitecenters): super().__init__(boxsize) self.numcrystallites = numcrystallites self.crystaltype = crystallitenames self.crystallitecenters = crystallitecenters self.crystallites = [Atoms() for n in range(numcrystallites)] self.crystalliteids = [[i] for i in range(numcrystallites)] def setgraintypes(self, typemap): self.graintypes = typemap def embedatoms(self, maskcrystal, grainid, keepatoms, compress=True): crystal = maskcrystal.copy() slicecrystal = crystal[keepatoms] natoms = slicecrystal.get_global_number_of_atoms() self.crystalliteids[grainid] = [grainid for i in range(natoms)] self.crystallites[grainid] = slicecrystal def compress(self): polycrystalbox = self.sidelens + self.angles self.polycrystal = Atoms(cell=polycrystalbox, pbc=True) for i, c in enumerate(self.crystallites): cids = self.crystalliteids[i] c.set_tags(cids) self.polycrystal += c self.natoms = self.polycrystal.get_global_number_of_atoms() chemsym = set(self.polycrystal.get_chemical_symbols()) self.chemmap = {c: i + 1 for i, c in enumerate(chemsym)} self.nspecies = len(chemsym) def pruneoverlap(self, criteria=0.5, verbose=False): ''' TODO: remove atoms overlapping assume PBC conditions''' print( "NOTICE: The pruning routine does not accoutn for stoichiometry restrictions." ) cutoff = neighborlist.natural_cutoffs(self.polycrystal) neighbors = neighborlist.NeighborList(cutoff, self_interaction=False, bothways=False) neighbors.update(self.polycrystal) isremoved = [] for a in self.polycrystal: ineigh = neighbors.get_neighbors(a.index) for j in ineigh[0]: r = self.polycrystal.get_distance(a.index, j, mic=True) if r < criteria and j not in isremoved: isremoved.append(j) if verbose: print("Atom ID %i removed due to overlap!" % (j)) del self.polycrystal[isremoved] self.natoms = self.polycrystal.get_global_number_of_atoms()
def test_atoms_distance(): # Setup a chain of H,O,C # H-O Dist = 2 # O-C Dist = 3 # C-H Dist = 5 with mic=False # C-H Dist = 4 with mic=True a = Atoms('HOC', positions=[(1, 1, 1), (3, 1, 1), (6, 1, 1)]) a.set_cell((9, 2, 2)) a.set_pbc((True, False, False)) # Calculate indiviually with mic=True assert a.get_distance(0, 1, mic=True) == 2 assert a.get_distance(1, 2, mic=True) == 3 assert a.get_distance(0, 2, mic=True) == 4 # Calculate indiviually with mic=False assert a.get_distance(0, 1, mic=False) == 2 assert a.get_distance(1, 2, mic=False) == 3 assert a.get_distance(0, 2, mic=False) == 5 # Calculate in groups with mic=True assert (a.get_distances(0, [1, 2], mic=True) == [2, 4]).all() # Calculate in groups with mic=False assert (a.get_distances(0, [1, 2], mic=False) == [2, 5]).all() # Calculate all with mic=True assert (a.get_all_distances(mic=True) == [[0, 2, 4], [2, 0, 3], [4, 3, 0]]).all() # Calculate all with mic=False assert (a.get_all_distances(mic=False) == [[0, 2, 5], [2, 0, 3], [5, 3, 0]]).all() # Scale Distance old = a.get_distance(0, 1) a.set_distance(0, 1, 0.9, add=True, factor=True) new = a.get_distance(0, 1) diff = new - 0.9 * old assert abs(diff) < 10e-6 # Change Distance old = a.get_distance(0, 1) a.set_distance(0, 1, 0.9, add=True) new = a.get_distance(0, 1) diff = new - old - 0.9 assert abs(diff) < 10e-6
def check_min_dist(totalsol, type='Defect', nat=None, min_len=0.7, STR=''): if type=='Defect' or type=='Crystal' or type=='Surface': if nat==None: nat=len(totalsol) cutoffs=[2.0 for one in totalsol] nl=NeighborList(cutoffs,bothways=True,self_interaction=False) nl.update(totalsol) for one in totalsol[0:nat]: nbatoms=Atoms() nbatoms.append(one) indices, offsets=nl.get_neighbors(one.index) for index, d in zip(indices,offsets): index = int(index) sym=totalsol[index].symbol pos=totalsol[index].position + numpy.dot(d,totalsol.get_cell()) at=Atom(symbol=sym,position=pos) nbatoms.append(at) while True: dflag=False for i in range(1,len(nbatoms)): d=nbatoms.get_distance(0,i) if d < min_len: nbatoms.set_distance(0,i,min_len+.01,fix=0.5) STR+='--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n' dflag=True if dflag==False: break for i in range(len(indices)): totalsol[indices[i]].position=nbatoms[i+1].position totalsol[one.index].position=nbatoms[0].position nl.update(totalsol) elif type=='Cluster': for i in range(len(totalsol)): for j in range(len(totalsol)): if i != j: d=totalsol.get_distance(i,j) if d < min_len: totalsol.set_distance(i,j,min_len,fix=0.5) STR+='--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n' else: print 'WARNING: In Check_Min_Dist in EvalEnergy: Structure Type not recognized' return totalsol, STR
def eval_energy(input): """Function to evaluate energy of an individual Inputs: input = [Optimizer class object with parameters, Individual class structure to be evaluated] Outputs: energy, bul, individ, signal energy = energy of Individual evaluated bul = bulk structure of Individual if simulation structure is Defect individ = Individual class structure evaluated signal = string of information about evaluation """ if input[0]==None: energy=0 bul=0 individ=0 rank = MPI.COMM_WORLD.Get_rank() signal='Evaluated none individual on '+repr(rank)+'\n' else: [Optimizer, individ]=input if Optimizer.calc_method=='MAST': energy = individ.energy bul = individ.energy signal = 'Recieved MAST structure\n' else: if Optimizer.parallel: rank = MPI.COMM_WORLD.Get_rank() if not Optimizer.genealogy: STR='----Individual ' + str(individ.index)+ ' Optimization----\n' else: STR='----Individual ' + str(individ.history_index)+ ' Optimization----\n' indiv=individ[0] if 'EE' in Optimizer.debug: debug = True else: debug = False if debug: write_xyz(Optimizer.debugfile,indiv,'Recieved by eval_energy') Optimizer.debugfile.flush() if Optimizer.structure=='Defect': indi=indiv.copy() if Optimizer.alloy==True: bulk=individ.bulki else: bulk=individ.bulko nat=indi.get_number_of_atoms() csize=bulk.get_cell() totalsol=Atoms(cell=csize, pbc=True) totalsol.extend(indi) totalsol.extend(bulk) for sym,c,m,u in Optimizer.atomlist: nc=len([atm for atm in totalsol if atm.symbol==sym]) STR+='Defect configuration contains '+repr(nc)+' '+repr(sym)+' atoms\n' elif Optimizer.structure=='Surface': totalsol=Atoms() totalsol.extend(indiv) nat=indiv.get_number_of_atoms() totalsol.extend(individ.bulki) for sym,c,m,u in Optimizer.atomlist: nc=len([atm for atm in totalsol if atm.symbol==sym]) STR+='Surface-Bulk configuration contains '+repr(nc)+' '+repr(sym)+' atoms\n' cell=numpy.maximum.reduce(indiv.get_cell()) totalsol.set_cell([cell[0],cell[1],500]) totalsol.set_pbc([True,True,False]) if Optimizer.constrain_position: ts = totalsol.copy() indc,indb,vacant,swap,stro = find_defects(ts,Optimizer.solidbulk,0) sbulk = Optimizer.solidbulk.copy() bcom = sbulk.get_center_of_mass() #totalsol.translate(-bulkcom) #indc.translate(-bulkcom) #totalsol.append(Atom(position=[0,0,0])) # for one in indc: # index = [atm.index for atm in totalsol if atm.position[0]==one.position[0] and atm.position[1]==one.position[1] and atm.position[2]==one.position[2]][0] # if totalsol.get_distance(-1,index) > Optimizer.sf: # r = random.random() # totalsol.set_distance(-1,index,Optimizer.sf*r,fix=0) # totalsol.pop() # totalsol.translate(bulkcom) com = indc.get_center_of_mass() dist = (sum((bcom[i] - com[i])**2 for i in range(3)))**0.5 if dist > Optimizer.sf: STR+='Shifting structure to within region\n' r = random.random()*Optimizer.sf comv = numpy.linalg.norm(com) ncom = [one*r/comv for one in com] trans = [ncom[i]-com[i] for i in range(3)] indices = [] for one in indc: id = [atm.index for atm in totalsol if atm.position[0]==one.position[0] and atm.position[1]==one.position[1] and atm.position[2]==one.position[2]][0] totalsol[id].position += trans # Check for atoms that are too close min_len=0.7 #pdb.set_trace() if not Optimizer.fixed_region: if Optimizer.structure=='Defect' or Optimizer.structure=='Surface': cutoffs=[2.0 for one in totalsol] nl=NeighborList(cutoffs,bothways=True,self_interaction=False) nl.update(totalsol) for one in totalsol[0:nat]: nbatoms=Atoms() nbatoms.append(one) indices, offsets=nl.get_neighbors(one.index) for index, d in zip(indices,offsets): index = int(index) sym=totalsol[index].symbol pos=totalsol[index].position + numpy.dot(d,totalsol.get_cell()) at=Atom(symbol=sym,position=pos) nbatoms.append(at) while True: dflag=False for i in range(1,len(nbatoms)): d=nbatoms.get_distance(0,i) if d < min_len: nbatoms.set_distance(0,i,min_len+.01,fix=0.5) STR+='--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n' dflag=True if dflag==False: break for i in range(len(indices)): totalsol[indices[i]].position=nbatoms[i+1].position totalsol[one.index].position=nbatoms[0].position nl.update(totalsol) if debug: write_xyz(Optimizer.debugfile,totalsol,'After minlength check') Optimizer.debugfile.flush() else: for i in range(len(indiv)): for j in range(len(indiv)): if i != j: d=indiv.get_distance(i,j) if d < min_len: indiv.set_distance(i,j,min_len,fix=0.5) STR+='--- WARNING: Atoms too close (<0.7A) - Implement Move ---\n' if debug: write_xyz(Optimizer.debugfile,indiv,'After minlength check') Optimizer.debugfile.flush() # Set calculator to use to get forces/energies if Optimizer.parallel: calc = setup_calculator(Optimizer) if Optimizer.fixed_region: pms=copy.deepcopy(calc.parameters) try: pms['mass'][len(pms['mass'])-1] += '\ngroup RO id >= '+repr(nat)+'\nfix freeze RO setforce 0.0 0.0 0.0\n' except KeyError: pms['pair_coeff'][0] += '\ngroup RO id >= '+repr(nat)+'\nfix freeze RO setforce 0.0 0.0 0.0\n' calc = LAMMPS(parameters=pms, files=calc.files, keep_tmp_files=calc.keep_tmp_files, tmp_dir=calc.tmp_dir) lmin = copy.copy(Optimizer.lammps_min) Optimizer.lammps_min = None Optimizer.static_calc = setup_calculator(Optimizer) Optimizer.lammps_min = lmin else: calc=Optimizer.calc if Optimizer.structure=='Defect' or Optimizer.structure=='Surface': totalsol.set_calculator(calc) totalsol.set_pbc(True) else: indiv.set_calculator(calc) indiv.set_pbc(True) #Current bug in ASE optimizer-Lammps prevents pbc=false if Optimizer.structure=='Cluster': indiv.set_cell([500,500,500]) indiv.translate([250,250,250]) cwd=os.getcwd() # Perform Energy Minimization if not Optimizer.parallel: Optimizer.output.flush() if Optimizer.ase_min == True: try: if Optimizer.structure=='Defect' or Optimizer.structure=='Surface': dyn=BFGS(totalsol) else: dyn=BFGS(indiv) dyn.run(fmax=Optimizer.ase_min_fmax, steps=Optimizer.ase_min_maxsteps) except OverflowError: STR+='--- Error: Infinite Energy Calculated - Implement Random ---\n' box=Atoms() indiv=gen_pop_box(Optimizer.natoms, Optimizer.atomlist, Optimizer.size) indiv.set_calculator(calc) dyn=BFGS(indiv) dyn.run(fmax=fmax, steps=steps) except numpy.linalg.linalg.LinAlgError: STR+='--- Error: Singular Matrix - Implement Random ---\n' indiv=gen_pop_box(Optimizer.natoms, Optimizer.atomlist, Optimizer.size) indiv.set_calculator(calc) dyn=BFGS(indiv) dyn.run(fmax=fmax, steps=steps) # Get Energy of Minimized Structure if Optimizer.structure=='Defect' or Optimizer.structure=='Surface': en=totalsol.get_potential_energy() #force=numpy.maximum.reduce(abs(totalsol.get_forces())) if Optimizer.fitness_scheme == 'enthalpyfit': pressure=totalsol.get_isotropic_pressure(totalsol.get_stress()) cell_max=numpy.maximum.reduce(totalsol.get_positions()) cell_min=numpy.minimum.reduce(totalsol.get_positions()) cell=cell_max-cell_min volume=cell[0]*cell[1]*cell[2] else: pressure=0 volume=0 na=totalsol.get_number_of_atoms() ena=en/na energy=en individ[0]=totalsol[0:nat] bul=totalsol[(nat):len(totalsol)] STR+='Number of positions = '+repr(len(bul)+len(individ[0]))+'\n' individ[0].set_cell(csize) indiv=individ[0] else: en=indiv.get_potential_energy() if Optimizer.fitness_scheme == 'enthalpyfit': pressure=indiv.get_isotropic_pressure(indiv.get_stress()) cell_max=numpy.maximum.reduce(indiv.get_positions()) cell_min=numpy.minimum.reduce(indiv.get_positions()) cell=cell_max-cell_min volume=cell[0]*cell[1]*cell[2] else: pressure=0 volume=0 na=indiv.get_number_of_atoms() ena=en/na energy=ena individ[0]=indiv bul=0 else: if Optimizer.structure=='Defect' or Optimizer.structure=='Surface': if Optimizer.calc_method=='VASP': en=totalsol.get_potential_energy() calcb=Vasp(restart=True) totalsol=calcb.get_atoms() stress=calcb.read_stress() else: try: totcop=totalsol.copy() if debug: write_xyz(Optimizer.debugfile,totcop,'Individual sent to lammps') OUT=totalsol.calc.calculate(totalsol) totalsol=OUT['atoms'] totalsol.set_pbc(True) if Optimizer.fixed_region: if debug: print 'Energy of fixed region calc = ', OUT['thermo'][-1]['pe'] totalsol.set_calculator(Optimizer.static_calc) OUT=totalsol.calc.calculate(totalsol) totalsol=OUT['atoms'] totalsol.set_pbc(True) if debug: print 'Energy of static calc = ', OUT['thermo'][-1]['pe'] en=OUT['thermo'][-1]['pe'] stress=numpy.array([OUT['thermo'][-1][i] for i in ('pxx','pyy','pzz','pyz','pxz','pxy')])*(-1e-4*GPa) #force=numpy.maximum.reduce(abs(totalsol.get_forces())) if debug: write_xyz(Optimizer.debugfile,totalsol,'After Lammps Minimization') Optimizer.debugfile.flush() except Exception, e: os.chdir(cwd) STR+='WARNING: Exception during energy eval:\n'+repr(e)+'\n' f=open('problem-structures.xyz','a') write_xyz(f,totcop,data='Starting structure hindex='+individ.history_index) write_xyz(f,totalsol,data='Lammps Min structure') en=10 stress=0 f.close() if Optimizer.fitness_scheme == 'enthalpyfit': pressure=totalsol.get_isotropic_pressure(stress) cell_max=numpy.maximum.reduce(totalsol.get_positions()) cell_min=numpy.minimum.reduce(totalsol.get_positions()) cell=cell_max-cell_min volume=cell[0]*cell[1]*cell[2] else: pressure=totalsol.get_isotropic_pressure(stress) volume=0 na=totalsol.get_number_of_atoms() ena=en/na energy=en if Optimizer.structure=='Defect': if Optimizer.fixed_region==True or Optimizer.finddefects==False: individ[0]=totalsol[0:nat] bul=totalsol[(nat):len(totalsol)] individ[0].set_cell(csize) else: if 'FI' in Optimizer.debug: outt=find_defects(totalsol,Optimizer.solidbulk,Optimizer.sf,atomlistcheck=Optimizer.atomlist,trackvacs=Optimizer.trackvacs,trackswaps=Optimizer.trackswaps,debug=Optimizer.debugfile) else: outt=find_defects(totalsol,Optimizer.solidbulk,Optimizer.sf,atomlistcheck=Optimizer.atomlist,trackvacs=Optimizer.trackvacs,trackswaps=Optimizer.trackswaps,debug=False) individ[0]=outt[0] bul=outt[1] individ.vacancies = outt[2] individ.swaps = outt[3] STR += outt[4] indiv=individ[0] else: top,bul=find_top_layer(totalsol,Optimizer.surftopthick) indiv=top.copy() individ[0]=top.copy() else: