def test_singlepointcalc(): """This test makes sure that the forces returned from a SinglePointCalculator are immutable. Previously, successive calls to atoms.get_forces(apply_constraint=x), with x alternating between True and False, would get locked into the constrained variation.""" def check_forces(): """Makes sure the unconstrained forces stay that way.""" forces = atoms.get_forces(apply_constraint=False) funconstrained = float(forces[0, 0]) forces = atoms.get_forces(apply_constraint=True) forces = atoms.get_forces(apply_constraint=False) funconstrained2 = float(forces[0, 0]) assert funconstrained2 == funconstrained atoms = fcc111('Cu', (2, 2, 1), vacuum=10.) atoms[0].x += 0.2 atoms.set_constraint(FixAtoms(indices=[atom.index for atom in atoms])) # First run the tes with EMT and save a force component. atoms.calc = EMT() check_forces() f = float(atoms.get_forces(apply_constraint=False)[0, 0]) # Save and reload with a SinglePointCalculator. atoms.write('singlepointtest.traj') atoms = read('singlepointtest.traj') check_forces() # Manually change a value. forces = atoms.get_forces(apply_constraint=False) forces[0, 0] = 42. forces = atoms.get_forces(apply_constraint=False) assert forces[0, 0] == f
def run_md_Morse(Morse_parameters, A0, steps=10000, trajectory="md.traj"): hbar_fs = (ase.units._hbar / ase.units._e) * 1.E15 D, a, R0, frequency = Morse_parameters[0:4] r0 = R0 calculator = MorsePotential2(a=a, D=D, r0=r0) #calculator = MorsePotential(rho0=6.0, epsilon=2.0, r0=1.0) period = (hbar_fs / frequency) / (2 * pi) pos = 1 * (r0 + A0) atoms = Atoms("HH", positions=[[0, 0, 0], [pos, 0, 0]], masses=[1.0, 1.0]) constr = FixAtoms(indices=[0]) atoms.set_constraint(constr) atoms.set_calculator(calculator) # def V(d): # atoms.set_positions([[0,0,0],[d,0,0]]) # return atoms.get_potential_energy() # r_plot = linspace(-4.0,4.0,1000) # V_plot = array([V(d) for d in r_plot]) # plt.plot(r_plot,V_plot) # plt.show() dynamics = VelocityVerlet(atoms, dt=(period / 20.) * ase.units.fs, trajectory=trajectory) dynamics.run(20000)
def construct_geometries(parent_calc, ml2relax): counter_calc = parent_calc # Initial structure guess initial_slab = fcc100("Cu", size=(2, 2, 3)) add_adsorbate(initial_slab, "C", 1.7, "hollow") initial_slab.center(axis=2, vacuum=4.0) mask = [atom.tag > 1 for atom in initial_slab] initial_slab.set_constraint(FixAtoms(mask=mask)) initial_slab.set_pbc(True) initial_slab.wrap(pbc=[True] * 3) initial_slab.set_calculator(counter_calc) # Final structure guess final_slab = initial_slab.copy() final_slab[-1].x += final_slab.get_cell()[0, 0] / 3 final_slab.set_calculator(counter_calc) if not ml2relax: print("BUILDING INITIAL") qn = BFGS( initial_slab, trajectory="initial.traj", logfile="initial_relax_log.txt" ) qn.run(fmax=0.01, steps=100) print("BUILDING FINAL") qn = BFGS(final_slab, trajectory="final.traj", logfile="final_relax_log.txt") qn.run(fmax=0.01, steps=100) initial_slab = read("initial.traj", "-1") final_slab = read("final.traj", "-1") # If there is already a pre-existing initial and final relaxed parent state we can read that to use as a starting point # initial_slab = read("/content/parent_initial.traj") # final_slab = read("/content/parent_final.traj") else: initial_slab = initial_slab final_slab = final_slab # initial_force_calls = counter_calc.force_calls return initial_slab, final_slab # , initial_force_calls
def test_dftb_relax_surface(): import os from ase.test import require from ase.test.testsuite import datafiles_directory from ase.build import diamond100 from ase.calculators.dftb import Dftb from ase.optimize import BFGS from ase.constraints import FixAtoms require('dftb') os.environ['DFTB_PREFIX'] = datafiles_directory calc = Dftb( label='dftb', kpts=(2, 2, 1), Hamiltonian_SCC='Yes', Hamiltonian_Filling='Fermi {', Hamiltonian_Filling_empty='Temperature [Kelvin] = 500.0', ) a = 5.40632280995384 atoms = diamond100('Si', (1, 1, 6), a=a, vacuum=6., orthogonal=True, periodic=True) atoms.positions[-2:, 2] -= 0.2 atoms.set_constraint(FixAtoms(indices=range(4))) atoms.set_calculator(calc) dyn = BFGS(atoms, logfile='-') dyn.run(fmax=0.1) e = atoms.get_potential_energy() assert abs(e - -214.036907) < 1., e
def test_replay(): from math import sqrt from ase import Atoms, Atom from ase.constraints import FixAtoms from ase.calculators.emt import EMT from ase.optimize import QuasiNewton from ase.io import read from ase.visualize import view # Distance between Cu atoms on a (100) surface: d = 3.6 / sqrt(2) a = Atoms('Cu', positions=[(0, 0, 0)], cell=(d, d, 1.0), pbc=(True, True, False)) a *= (2, 2, 1) # 2x2 (100) surface-cell # Approximate height of Ag atom on Cu(100) surfece: h0 = 2.0 a += Atom('Ag', (d / 2, d / 2, h0)) if 0: view(a) constraint = FixAtoms(range(len(a) - 1)) a.calc = EMT() a.set_constraint(constraint) dyn1 = QuasiNewton(a, trajectory='AgCu1.traj', logfile='AgCu1.log') dyn1.run(fmax=0.1) a = read('AgCu1.traj') a.calc = EMT() print(a.constraints) dyn2 = QuasiNewton(a, trajectory='AgCu2.traj', logfile='AgCu2.log') dyn2.replay_trajectory('AgCu1.traj') dyn2.run(fmax=0.01)
def get_atoms(self, frame, remove_hidden=False): atoms = Atoms(positions=self.P[frame], numbers=self.Z, magmoms=self.M[0], tags=self.T[frame], cell=self.A[frame], pbc=self.pbc) if not np.isnan(self.V).any(): atoms.set_velocities(self.V[frame]) # check for constrained atoms and add them accordingly: if not self.dynamic.all(): atoms.set_constraint(FixAtoms(mask=1 - self.dynamic)) # Remove hidden atoms if applicable if remove_hidden: atoms = atoms[self.visible] f = self.F[frame][self.visible] else: f = self.F[frame] atoms.set_calculator( SinglePointCalculator(atoms, energy=self.E[frame], forces=f)) return atoms
def slabgen(termination, size, adsorbate, position): if termination == '100': prim = fcc100('Cu', a=3.6302862146117354, size=(1, 1, 5), vacuum=15, orthogonal=True, periodic=True) elif termination == '111': prim = fcc111_root('Cu', root=3, a=3.6302862146117354, size=(1, 1, 5), vacuum=15) super = make_supercell(prim, size) add_adsorbate(slab=super, adsorbate=adsorbate, height=ads_height, position=position) constr = FixAtoms( indices=[atom.index for atom in super if atom.position[2] < 19]) super.set_constraint(constr) return super
def optimize(self, atoms, name): opts = self.opts if opts.constrain_tags: tags = [int(t) for t in opts.constrain_tags.split(',')] mask = [t in tags for t in atoms.get_tags()] atoms.constraints = FixAtoms(mask=mask) trajectory = PickleTrajectory(self.get_filename(name, 'traj'), 'w', atoms) if opts.maximum_stress: optimizer = LBFGS(UnitCellFilter(atoms), logfile=self.logfile) fmax = opts.maximum_stress else: optimizer = LBFGS(atoms, logfile=self.logfile) fmax = opts.maximum_force optimizer.attach(trajectory) optimizer.run(fmax=fmax) data = {} if hasattr(optimizer, 'force_calls'): data['force_calls'] = optimizer.force_calls return data
def test_dftb_relax_surface(factory): calc = factory.calc( label='dftb', kpts=(2, 2, 1), Hamiltonian_SCC='Yes', Hamiltonian_Filling='Fermi {', Hamiltonian_Filling_empty='Temperature [Kelvin] = 500.0', ) a = 5.40632280995384 atoms = diamond100('Si', (1, 1, 6), a=a, vacuum=6., orthogonal=True, periodic=True) atoms.positions[-2:, 2] -= 0.2 atoms.set_constraint(FixAtoms(indices=range(4))) atoms.calc = calc dyn = BFGS(atoms, logfile='-') dyn.run(fmax=0.1) e = atoms.get_potential_energy() assert abs(e - -214.036907) < 1., e
def aual100(site, height, calc=None): slab = fcc100('Al', size=(2, 2, 2)) slab.center(axis=2, vacuum=3.0) add_adsorbate(slab, 'Au', height, site) mask = [atom.symbol == 'Al' for atom in slab] fixlayer = FixAtoms(mask=mask) slab.set_constraint(fixlayer) if calc is None: calc = GPAW(mode=PW(200), kpts=(2, 2, 1), xc='PBE', txt=site + '.txt', eigensolver='rmm-diis', nbands=40) slab.set_calculator(calc) qn = QuasiNewton(slab, trajectory=site + calc.name + '.traj') qn.run(fmax=0.05) if isinstance(calc, GPAW): calc.write(site + '.gpw') return slab.get_potential_energy()
def read_aims(filename): """Import FHI-aims geometry type files. Reads unitcell, atom positions and constraints from a geometry.in file. """ from ase import Atoms from ase.constraints import FixAtoms, FixCartesian import numpy as np atoms = Atoms() fd = open(filename, 'r') lines = fd.readlines() fd.close() positions = [] cell = [] symbols = [] magmoms = [] fix = [] fix_cart = [] xyz = np.array([0, 0, 0]) i = -1 n_periodic = -1 periodic = np.array([False, False, False]) cart_positions, scaled_positions = False, False for n, line in enumerate(lines): inp = line.split() if inp == []: continue if inp[0] == 'atom': cart_positions = True if xyz.all(): fix.append(i) elif xyz.any(): fix_cart.append(FixCartesian(i, xyz)) floatvect = float(inp[1]), float(inp[2]), float(inp[3]) positions.append(floatvect) symbols.append(inp[-1]) i += 1 xyz = np.array([0, 0, 0]) if inp[0] == 'atom_frac': scaled_positions = True if xyz.all(): fix.append(i) elif xyz.any(): fix_cart.append(FixCartesian(i, xyz)) floatvect = float(inp[1]), float(inp[2]), float(inp[3]) positions.append(floatvect) symbols.append(inp[-1]) i += 1 xyz = np.array([0, 0, 0]) elif inp[0] == 'lattice_vector': floatvect = float(inp[1]), float(inp[2]), float(inp[3]) cell.append(floatvect) n_periodic = n_periodic + 1 periodic[n_periodic] = True elif inp[0] == 'initial_moment': magmoms.append(float(inp[1])) if inp[0] == 'constrain_relaxation': if inp[1] == '.true.': fix.append(i) elif inp[1] == 'x': xyz[0] = 1 elif inp[1] == 'y': xyz[1] = 1 elif inp[1] == 'z': xyz[2] = 1 if xyz.all(): fix.append(i) elif xyz.any(): fix_cart.append(FixCartesian(i, xyz)) if cart_positions and scaled_positions: raise Exception("Can't specify atom positions with mixture of " 'Cartesian and fractional coordinates') elif scaled_positions and periodic.any(): atoms = Atoms(symbols, scaled_positions=positions, cell=cell, pbc=periodic) else: atoms = Atoms(symbols, positions) if len(magmoms) > 0: atoms.set_initial_magnetic_moments(magmoms) if periodic.any(): atoms.set_cell(cell) atoms.set_pbc(periodic) if len(fix): atoms.set_constraint([FixAtoms(indices=fix)] + fix_cart) else: atoms.set_constraint(fix_cart) return atoms
from vasp import Vasp from ase.lattice.surface import fcc110 from ase.io import write from ase.constraints import FixAtoms atoms = fcc110('Au', size=(2, 1, 6), vacuum=10.0) constraint = FixAtoms(mask=[atom.tag > 2 for atom in atoms]) atoms.set_constraint(constraint) write('images/Au-110.png', atoms.repeat((2, 2, 1)), rotation='-90x', show_unit_cell=2) print Vasp('surfaces/Au-110', xc='PBE', kpts=[6, 6, 1], encut=350, ibrion=2, isif=2, nsw=10, atoms=atoms).potential_energy
from ase.build import fcc100, add_adsorbate from ase.constraints import FixAtoms from ase.calculators.emt import EMT from ase.optimize import QuasiNewton # 2x2-Al(001) surface with 3 layers and an # Au atom adsorbed in a hollow site: slab = fcc100('Al', size=(2, 2, 3)) add_adsorbate(slab, 'Au', 1.7, 'hollow') slab.center(axis=2, vacuum=4.0) # Make sure the structure is correct: #view(slab) # Fix second and third layers: mask = [atom.tag > 1 for atom in slab] #print(mask) slab.set_constraint(FixAtoms(mask=mask)) # Use EMT potential: slab.set_calculator(EMT()) # Initial state: qn = QuasiNewton(slab, trajectory='initial.traj') qn.run(fmax=0.05) # Final state: slab[-1].x += slab.get_cell()[0, 0] / 2 qn = QuasiNewton(slab, trajectory='final.traj') qn.run(fmax=0.05)
import numpy as np from ase.io import read from ase.constraints import FixAtoms from ase.calculators.emt import EMT from ase.neb import NEB from ase.optimize.fire import FIRE as QuasiNewton initial = read('N2.traj') final = read('2N.traj') configs = [initial.copy() for i in range(8)] + [final] constraint = FixAtoms(mask=[atom.symbol != 'N' for atom in initial]) for config in configs: config.set_calculator(EMT()) config.set_constraint(constraint) band = NEB(configs) band.interpolate() # Create a quickmin object: relax = QuasiNewton(band) relax.run(steps=20) e0 = initial.get_potential_energy() for config in configs: d = config[-2].position - config[-1].position print np.linalg.norm(d), config.get_potential_energy() - e0
def test_database_logic(seed, testdir): from ase.ga.data import PrepareDB from ase.ga.data import DataConnection from ase.ga.startgenerator import StartGenerator from ase.ga.utilities import closest_distances_generator from ase.ga import set_raw_score import numpy as np from ase.build import fcc111 from ase.constraints import FixAtoms # set up the random number generator rng = np.random.RandomState(seed) slab = fcc111('Au', size=(4, 4, 2), vacuum=10.0, orthogonal=True) slab.set_constraint(FixAtoms(mask=slab.positions[:, 2] <= 10.)) # define the volume in which the adsorbed cluster is optimized # the volume is defined by a corner position (p0) # and three spanning vectors (v1, v2, v3) pos = slab.get_positions() cell = slab.get_cell() p0 = np.array([0., 0., max(pos[:, 2]) + 2.]) v1 = cell[0, :] * 0.8 v2 = cell[1, :] * 0.8 v3 = cell[2, :] v3[2] = 3. # define the closest distance between two atoms of a given species blmin = closest_distances_generator(atom_numbers=[47, 79], ratio_of_covalent_radii=0.7) # Define the composition of the atoms to optimize atom_numbers = 2 * [47] + 2 * [79] # create the starting population sg = StartGenerator(slab=slab, blocks=atom_numbers, blmin=blmin, box_to_place_in=[p0, [v1, v2, v3]], rng=rng) # generate the starting population starting_population = [sg.get_new_candidate() for i in range(20)] d = PrepareDB(db_file_name=db_file, simulation_cell=slab, stoichiometry=atom_numbers) for a in starting_population: d.add_unrelaxed_candidate(a) # and now for the actual test dc = DataConnection(db_file) dc.get_slab() dc.get_atom_numbers_to_optimize() assert dc.get_number_of_unrelaxed_candidates() == 20 a1 = dc.get_an_unrelaxed_candidate() dc.mark_as_queued(a1) assert dc.get_number_of_unrelaxed_candidates() == 19 assert len(dc.get_all_candidates_in_queue()) == 1 set_raw_score(a1, 0.0) dc.add_relaxed_step(a1) assert dc.get_number_of_unrelaxed_candidates() == 19 assert len(dc.get_all_candidates_in_queue()) == 0 assert len(dc.get_all_relaxed_candidates()) == 1 a2 = dc.get_an_unrelaxed_candidate() dc.mark_as_queued(a2) confid = a2.info['confid'] assert dc.get_all_candidates_in_queue()[0] == confid dc.remove_from_queue(confid) assert len(dc.get_all_candidates_in_queue()) == 0
file_name = file_name_py.replace('.py', '') file_traj = file_name + '.traj' start_file = '../In2O3_110_clean.traj' mode = 'a' try: sys = Trajectory(file_traj, 'r')[-1] except (IOError, RuntimeError): print "Importing trajectory file from: %s" % start_file sys = read(start_file) sys.center(vacuum=5.0, axis=2) else: print "Importing trajectory file from: %s" % file_traj set_calc_In2O3(sys) print "Constraints:" print " c1: Fix bottom two layers." avg_z = np.mean([atom.z for atom in sys]) c1 = FixAtoms(mask=[atom.z < avg_z for atom in sys]) sys.set_constraint(c1) if testRun == True: run_testRun(sys) else: geo_traj = Trajectory(file_traj, mode, sys) dyn = LBFGS(sys, trajectory=geo_traj) dyn.run(fmax=0.05) energy = sys.get_potential_energy() print "Energy: %f" % energy print "Completed %s" % file_name_py
from ase.build import fcc211, add_adsorbate from ase.constraints import FixAtoms from ase.calculators.emt import EMT from ase.optimize import QuasiNewton from ase.neb import NEBTools from ase.autoneb import AutoNEB # Pt atom adsorbed in a hollow site: slab = fcc211('Pt', size=(3, 2, 2), vacuum=4.0) add_adsorbate(slab, 'Pt', 0.5, (-0.1, 2.7)) # Fix second and third layers: slab.set_constraint(FixAtoms(range(6, 12))) # Use EMT potential: slab.set_calculator(EMT()) # Initial state: qn = QuasiNewton(slab, trajectory='neb000.traj') qn.run(fmax=0.05) # Final state: slab[-1].x += slab.get_cell()[0, 0] slab[-1].y += 2.8 qn = QuasiNewton(slab, trajectory='neb001.traj') qn.run(fmax=0.05) # Stops PermissionError on Win32 for access to # the traj file that remains open. del qn
def _read_xyz_frame(lines, natoms, properties_parser=key_val_str_to_dict, nvec=0): # comment line line = next(lines).strip() if nvec > 0: info = {'comment': line} else: info = properties_parser(line) if line else {} pbc = None if 'pbc' in info: pbc = info['pbc'] del info['pbc'] elif 'Lattice' in info: # default pbc for extxyz file containing Lattice # is True in all directions pbc = [True, True, True] elif nvec > 0: # cell information given as pseudo-Atoms pbc = [False, False, False] cell = None if 'Lattice' in info: # NB: ASE cell is transpose of extended XYZ lattice cell = info['Lattice'].T del info['Lattice'] elif nvec > 0: # cell information given as pseudo-Atoms cell = np.zeros((3, 3)) if 'Properties' not in info: # Default set of properties is atomic symbols and positions only info['Properties'] = 'species:S:1:pos:R:3' properties, names, dtype, convs = parse_properties(info['Properties']) del info['Properties'] data = [] for ln in range(natoms): try: line = next(lines) except StopIteration: raise XYZError( 'ase.io.extxyz: Frame has {} atoms, expected {}'.format( len(data), natoms)) vals = line.split() row = tuple([conv(val) for conv, val in zip(convs, vals)]) data.append(row) try: data = np.array(data, dtype) except TypeError: raise XYZError('Badly formatted data ' 'or end of file reached before end of frame') # Read VEC entries if present if nvec > 0: for ln in range(nvec): try: line = next(lines) except StopIteration: raise XYZError( 'ase.io.adfxyz: Frame has {} cell vectors, expected {}'. format(len(cell), nvec)) entry = line.split() if not entry[0].startswith('VEC'): raise XYZError('Expected cell vector, got {}'.format(entry[0])) try: n = int(entry[0][3:]) if n != ln + 1: raise XYZError('Expected VEC{}, got VEC{}'.format( ln + 1, n)) except: raise XYZError('Expected VEC{}, got VEC{}'.format( ln + 1, entry[0][3:])) cell[ln] = np.array([float(x) for x in entry[1:]]) pbc[ln] = True if nvec != pbc.count(True): raise XYZError('Problem with number of cell vectors') pbc = tuple(pbc) arrays = {} for name in names: ase_name, cols = properties[name] if cols == 1: value = data[name] else: value = np.vstack([data[name + str(c)] for c in range(cols)]).T arrays[ase_name] = value symbols = None if 'symbols' in arrays: symbols = [s.capitalize() for s in arrays['symbols']] del arrays['symbols'] numbers = None duplicate_numbers = None if 'numbers' in arrays: if symbols is None: numbers = arrays['numbers'] else: duplicate_numbers = arrays['numbers'] del arrays['numbers'] charges = None if 'charges' in arrays: charges = arrays['charges'] del arrays['charges'] positions = None if 'positions' in arrays: positions = arrays['positions'] del arrays['positions'] atoms = Atoms(symbols=symbols, positions=positions, numbers=numbers, charges=charges, cell=cell, pbc=pbc, info=info) # Read and set constraints if 'move_mask' in arrays: if properties['move_mask'][1] == 3: atoms.set_constraint([ FixCartesian(a, mask=arrays['move_mask'][a, :]) for a in range(natoms) ]) elif properties['move_mask'][1] == 1: atoms.set_constraint(FixAtoms(mask=~arrays['move_mask'])) else: raise XYZError('Not implemented constraint') del arrays['move_mask'] for name, array in arrays.items(): atoms.new_array(name, array) if duplicate_numbers is not None: atoms.set_atomic_numbers(duplicate_numbers) # Load results of previous calculations into SinglePointCalculator results = {} for key in list(atoms.info.keys()): if key in all_properties: results[key] = atoms.info[key] # special case for stress- convert to Voigt 6-element form if key.startswith('stress') and results[key].shape == (3, 3): stress = results[key] stress = np.array([ stress[0, 0], stress[1, 1], stress[2, 2], stress[1, 2], stress[0, 2], stress[0, 1] ]) results[key] = stress for key in list(atoms.arrays.keys()): if key in all_properties: results[key] = atoms.arrays[key] if results != {}: calculator = SinglePointCalculator(atoms, **results) atoms.set_calculator(calculator) return atoms
def read_aims_output(filename, index=-1): """Import FHI-aims output files with all data available, i.e. relaxations, MD information, force information etc etc etc.""" from ase import Atoms, Atom from ase.calculators.singlepoint import SinglePointCalculator from ase.units import Ang, fs from ase.constraints import FixAtoms, FixCartesian molecular_dynamics = False fd = open(filename, 'r') cell = [] images = [] fix = [] fix_cart = [] f = None pbc = False found_aims_calculator = False v_unit = Ang / (1000.0 * fs) while True: line = fd.readline() if not line: break # if "List of parameters used to initialize the calculator:" in line: # fd.readline() # calc = read_aims_calculator(fd) # calc.out = filename # found_aims_calculator = True if "| Number of atoms :" in line: inp = line.split() n_atoms = int(inp[5]) if "| Unit cell:" in line: if not pbc: pbc = True for i in range(3): inp = fd.readline().split() cell.append([inp[1], inp[2], inp[3]]) if "Found relaxation constraint for atom" in line: xyz = [0, 0, 0] ind = int(line.split()[5][:-1]) - 1 if "All coordinates fixed" in line: if ind not in fix: fix.append(ind) if "coordinate fixed" in line: coord = line.split()[6] if coord == 'x': xyz[0] = 1 elif coord == 'y': xyz[1] = 1 elif coord == 'z': xyz[2] = 1 keep = True for n, c in enumerate(fix_cart): if ind == c.a: keep = False if keep: fix_cart.append(FixCartesian(ind, xyz)) else: fix_cart[n].mask[xyz.index(1)] = 0 if "Atomic structure:" in line and not molecular_dynamics: fd.readline() atoms = Atoms() for i in range(n_atoms): inp = fd.readline().split() atoms.append(Atom(inp[3], (inp[4], inp[5], inp[6]))) if "Complete information for previous time-step:" in line: molecular_dynamics = True if "Updated atomic structure:" in line and not molecular_dynamics: fd.readline() atoms = Atoms() velocities = [] for i in range(n_atoms): inp = fd.readline().split() if 'lattice_vector' in inp[0]: cell = [] for i in range(3): cell += [[float(inp[1]), float(inp[2]), float(inp[3])]] inp = fd.readline().split() atoms.set_cell(cell) inp = fd.readline().split() atoms.append(Atom(inp[4], (inp[1], inp[2], inp[3]))) if molecular_dynamics: inp = fd.readline().split() if "Atomic structure (and velocities)" in line: fd.readline() atoms = Atoms() velocities = [] for i in range(n_atoms): inp = fd.readline().split() atoms.append(Atom(inp[4], (inp[1], inp[2], inp[3]))) inp = fd.readline().split() velocities += [[ float(inp[1]) * v_unit, float(inp[2]) * v_unit, float(inp[3]) * v_unit ]] atoms.set_velocities(velocities) if len(fix): atoms.set_constraint([FixAtoms(indices=fix)] + fix_cart) else: atoms.set_constraint(fix_cart) images.append(atoms) if "Total atomic forces" in line: f = [] for i in range(n_atoms): inp = fd.readline().split() f.append([float(inp[2]), float(inp[3]), float(inp[4])]) if not found_aims_calculator: e = images[-1].get_potential_energy() images[-1].set_calculator( SinglePointCalculator(atoms, energy=e, forces=f)) e = None f = None if "Total energy corrected" in line: e = float(line.split()[5]) if pbc: atoms.set_cell(cell) atoms.pbc = True if not found_aims_calculator: atoms.set_calculator(SinglePointCalculator(atoms, energy=e)) if not molecular_dynamics: if len(fix): atoms.set_constraint([FixAtoms(indices=fix)] + fix_cart) else: atoms.set_constraint(fix_cart) images.append(atoms) e = None # if found_aims_calculator: # calc.set_results(images[-1]) # images[-1].set_calculator(calc) fd.close() if molecular_dynamics: images = images[1:] # return requested images, code borrowed from ase/io/trajectory.py if isinstance(index, int): return images[index] else: step = index.step or 1 if step > 0: start = index.start or 0 if start < 0: start += len(images) stop = index.stop or len(images) if stop < 0: stop += len(images) else: if index.start is None: start = len(images) - 1 else: start = index.start if start < 0: start += len(images) if index.stop is None: stop = -1 else: stop = index.stop if stop < 0: stop += len(images) return [images[i] for i in range(start, stop, step)]
def test_thermochemistry(): """Tests of the major methods (HarmonicThermo, IdealGasThermo, CrystalThermo) from the thermochemistry module.""" # Ideal gas thermo. atoms = Atoms('N2', positions=[(0, 0, 0), (0, 0, 1.1)], calculator=EMT()) QuasiNewton(atoms).run(fmax=0.01) energy = atoms.get_potential_energy() vib = Vibrations(atoms, name='idealgasthermo-vib') vib.run() vib_energies = vib.get_energies() thermo = IdealGasThermo(vib_energies=vib_energies, geometry='linear', atoms=atoms, symmetrynumber=2, spin=0, potentialenergy=energy) thermo.get_gibbs_energy(temperature=298.15, pressure=2 * 101325.) # Harmonic thermo. atoms = fcc100('Cu', (2, 2, 2), vacuum=10.) atoms.set_calculator(EMT()) add_adsorbate(atoms, 'Pt', 1.5, 'hollow') atoms.set_constraint( FixAtoms(indices=[atom.index for atom in atoms if atom.symbol == 'Cu'])) QuasiNewton(atoms).run(fmax=0.01) vib = Vibrations( atoms, name='harmonicthermo-vib', indices=[atom.index for atom in atoms if atom.symbol != 'Cu']) vib.run() vib.summary() vib_energies = vib.get_energies() thermo = HarmonicThermo(vib_energies=vib_energies, potentialenergy=atoms.get_potential_energy()) thermo.get_helmholtz_energy(temperature=298.15) # Crystal thermo. atoms = bulk('Al', 'fcc', a=4.05) calc = EMT() atoms.set_calculator(calc) energy = atoms.get_potential_energy() # Phonon calculator N = 7 ph = Phonons(atoms, calc, supercell=(N, N, N), delta=0.05) ph.run() ph.read(acoustic=True) phonon_energies, phonon_DOS = ph.dos(kpts=(4, 4, 4), npts=30, delta=5e-4) thermo = CrystalThermo(phonon_energies=phonon_energies, phonon_DOS=phonon_DOS, potentialenergy=energy, formula_units=4) thermo.get_helmholtz_energy(temperature=298.15) # Hindered translator / rotor. # (Taken directly from the example given in the documentation.) vibs = np.array([ 3049.060670, 3040.796863, 3001.661338, 2997.961647, 2866.153162, 2750.855460, 1436.792655, 1431.413595, 1415.952186, 1395.726300, 1358.412432, 1335.922737, 1167.009954, 1142.126116, 1013.918680, 803.400098, 783.026031, 310.448278, 136.112935, 112.939853, 103.926392, 77.262869, 60.278004, 25.825447 ]) vib_energies = vibs / 8065.54429 # Convert to eV from cm^-1. trans_barrier_energy = 0.049313 # eV rot_barrier_energy = 0.017675 # eV sitedensity = 1.5e15 # cm^-2 rotationalminima = 6 symmetrynumber = 1 mass = 30.07 # amu inertia = 73.149 # amu Ang^-2 thermo = HinderedThermo(vib_energies=vib_energies, trans_barrier_energy=trans_barrier_energy, rot_barrier_energy=rot_barrier_energy, sitedensity=sitedensity, rotationalminima=rotationalminima, symmetrynumber=symmetrynumber, mass=mass, inertia=inertia) helmholtz = thermo.get_helmholtz_energy(temperature=298.15) target = 1.593 # Taken from documentation example. assert (helmholtz - target) < 0.001
def make_barrier_configurations(pot_path=None, calculator=None, cylinder_r=10, hard_core=False): """ Creates the initial and final configurations for the NEB calculation The positions in FixedAtoms contrained region are average between final and inital configurations Parameters ---------- pot_path : sting Path to the potential file. calculator : type Description of parameter `calculator`. cylinder_r : float Radius of cylinder of unconstrained atoms around the dislocation in angstrom. hard_core : bool Type of the core hard or soft. If hard is chosen the displacment field is teversed. Returns ------- disloc_ini : ase.Atoms Initial dislocation configuration. disloc_fin : ase.Atoms Final dislocation configuration. bulk : ase.Atoms Perfect bulk configuration for Vitek displacement maps """ if pot_path is not None: alat, C11, C12, C44 = get_elastic_constants(pot_path=pot_path) # get the cutoff from the potential file with open(pot_path) as potfile: for i, tmp_str in enumerate(potfile): if i == 4: # read the last number in the fifth line cutoff = float(tmp_str.split()[-1]) break elif calculator is not None: alat, C11, C12, C44 = get_elastic_constants(calculator=calculator) cutoff = 5.0 # the value for trainig data for GAP from paper cent_x = np.sqrt(6.0)*alat/3.0 center = (cent_x, 0.0, 0.0) disloc_ini, bulk_ini, __ = make_screw_cyl(alat, C11, C12, C44, cylinder_r=cylinder_r, cutoff=cutoff, hard_core=hard_core, l_extend=center) disloc_fin, __, __ = make_screw_cyl(alat, C11, C12, C44, cylinder_r=cylinder_r, cutoff=cutoff, hard_core=hard_core, center=center) # get the fixed atoms constrain FixAtoms = disloc_ini.constraints[0] # get the indices of fixed atoms fixed_atoms_indices = FixAtoms.get_indices() # make the average position of fixed atoms # between initial and the lastlast position ini_fix_pos = disloc_ini.get_positions()[fixed_atoms_indices] fin_fix_pos = disloc_fin.get_positions()[fixed_atoms_indices] new_av_pos = (ini_fix_pos + fin_fix_pos)/2.0 positions = disloc_ini.get_positions() positions[fixed_atoms_indices] = new_av_pos disloc_ini.set_positions(positions, apply_constraint=False) positions = disloc_fin.get_positions() positions[fixed_atoms_indices] = new_av_pos disloc_fin.set_positions(positions, apply_constraint=False) return disloc_fin, disloc_ini, bulk_ini
# Add the Cu2 adsorbate. adsorbate = Atoms([ Atom('Cu', atoms[7].position + (0., 0., 2.5)), Atom('Cu', atoms[7].position + (0.5, 0.5, 4.5)) ]) atoms.extend(adsorbate) atoms.write('init.cif') # 输出初始结构 COM index = [atom.index for atom in atoms if atom.symbol == 'Cu'] masses = atoms.get_masses()[index] com_init = np.dot(masses, atoms.positions[index]) / masses.sum() # Constrain the surface to be fixed and a FixAtomsCom or FixAtomsComXY constraint between # the adsorbate atoms. constraints = [ FixAtoms(indices=[atom.index for atom in atoms if atom.symbol == 'Pt']), FixAtomsComXY( indices=[atom.index for atom in atoms if atom.symbol == 'Cu']), ] atoms.set_constraint(constraints) calc = EMT() atoms.set_calculator(calc) dyn = BFGS(atoms, trajectory='bfgs.traj') dyn.run(fmax=0.0001) atoms.write('final.cif') # 输出初始结构 COM com_final = np.dot(masses, atoms.positions[index]) / masses.sum() print('Center of Mass for initial structure:', com_init) print('Center of Mass for final structure:', com_final)
from ase.io import read from ase.constraints import FixAtoms from ase.neb import NEB from JDFTx import JDFTx from ase.optimize import BFGS from ase.parallel import rank, size initial = read('initial.traj') final = read('final.traj') constraint = FixAtoms(mask=[atom.tag > 1 for atom in initial]) images = [initial] for i in range(3): image = initial.copy() image.set_calculator(JDFTx(executable='mpirun -n 1 -N 1 -c 12 --exclude=node[1001-1032] /home/jfm343/JDFTXDIR/build/jdftx', pseudoDir='/home/jfm343/JDFTXDIR/build/pseudopotentials',pseudoSet='GBRV-pbe',commands={'elec-cutoff' : '20 100','kpoint-folding' : '2 2 1'})) image.set_constraint(constraint) images.append(image) images.append(final) neb = NEB(images, parallel=True) neb.interpolate() qn = BFGS(neb, trajectory='neb.traj') qn.run(fmax=0.05)
from ase.test import must_raise for name in ['y2.json', 'y2.db']: c = connect(name) print(name, c) id = c.reserve(abc=7) c.delete([d.id for d in c.select(abc=7)]) id = c.reserve(abc=7) assert c[id].abc == 7 a = c.get_atoms(id) c.write(Atoms()) ch4 = molecule('CH4', calculator=EMT()) ch4.constraints = [FixAtoms(indices=[1]), FixBondLength(0, 2)] f1 = ch4.get_forces() print(f1) c.delete([d.id for d in c.select(C=1)]) chi = np.array([1 + 0.5j, 0.5]) id = c.write(ch4, data={'1-butyne': 'bla-bla', 'chi': chi}) row = c.get(id) print(row.data['1-butyne'], row.data.chi) assert (row.data.chi == chi).all() assert len(c.get_atoms(C=1).constraints) == 2 f2 = c.get(C=1).forces
return line.split()[-2] else: RC = True if RC: with open(DB_dir + M + '/bulk.out') as f: for line in f: if 'Lattice constant:' in line: return line.split()[-2] metal = ['Bi'] metal = sorted(metal) data = {m: get_a(m) for m in metal} print(data) db = connect('A_slab.db') for k, v in data.items(): slab = fcc111(k, (2, 2, 3), float(v), 10) c = FixAtoms(indices=[a.index for a in slab if a.tag > 1]) slab.set_constraint(c) slab.wrap() parameters['spinpol'] = False keys['metal'] = k db.write(slab, key_value_pairs=keys, data=parameters)
from gpaw import GPAW, PW # Initial state: # 2x2-Al(001) surface with 1 layer and an # Au atom adsorbed in a hollow site: slab = fcc100('Al', size=(2, 2, 2)) slab.center(axis=2, vacuum=3.0) add_adsorbate(slab, 'Au', 1.6, 'hollow') # Make sure the structure is correct: view(slab) # Fix the Al atoms: mask = [atom.symbol == 'Al' for atom in slab] print(mask) fixlayer = FixAtoms(mask=mask) slab.set_constraint(fixlayer) # Use GPAW: calc = GPAW(mode=PW(200), kpts=(2, 2, 1), xc='PBE', txt='hollow.txt') slab.set_calculator(calc) qn = QuasiNewton(slab, trajectory='hollow.traj') # Find optimal height. The stopping criterion is: the force on the # Au atom should be less than 0.05 eV/Ang qn.run(fmax=0.05) calc.write('hollow.gpw') # Write gpw output after the minimization print('energy:', slab.get_potential_energy())
def read_aims_output(filename, index=-1): """Import FHI-aims output files with all data available, i.e. relaxations, MD information, force information etc etc etc.""" from ase import Atoms, Atom from ase.calculators.singlepoint import SinglePointCalculator from ase.constraints import FixAtoms, FixCartesian molecular_dynamics = False fd = open(filename, "r") cell = [] images = [] fix = [] fix_cart = [] f = None pbc = False found_aims_calculator = False stress = None for line in fd: # if "List of parameters used to initialize the calculator:" in line: # next(fd) # calc = read_aims_calculator(fd) # calc.out = filename # found_aims_calculator = True if "| Number of atoms :" in line: inp = line.split() n_atoms = int(inp[5]) if "| Unit cell:" in line: if not pbc: pbc = True for i in range(3): inp = next(fd).split() cell.append([inp[1], inp[2], inp[3]]) if "Found relaxation constraint for atom" in line: xyz = [0, 0, 0] ind = int(line.split()[5][:-1]) - 1 if "All coordinates fixed" in line: if ind not in fix: fix.append(ind) if "coordinate fixed" in line: coord = line.split()[6] if coord == "x": xyz[0] = 1 elif coord == "y": xyz[1] = 1 elif coord == "z": xyz[2] = 1 keep = True for n, c in enumerate(fix_cart): if ind == c.a: keep = False if keep: fix_cart.append(FixCartesian(ind, xyz)) else: fix_cart[n].mask[xyz.index(1)] = 0 if "Atomic structure:" in line and not molecular_dynamics: next(fd) atoms = Atoms() for _ in range(n_atoms): inp = next(fd).split() atoms.append(Atom(inp[3], (inp[4], inp[5], inp[6]))) if "Complete information for previous time-step:" in line: molecular_dynamics = True if "Updated atomic structure:" in line and not molecular_dynamics: atoms = _parse_atoms(fd, n_atoms=n_atoms) elif "Atomic structure (and velocities)" in line: next(fd) atoms = Atoms() velocities = [] for i in range(n_atoms): inp = next(fd).split() atoms.append(Atom(inp[4], (inp[1], inp[2], inp[3]))) inp = next(fd).split() floatvect = [v_unit * float(l) for l in inp[1:4]] velocities.append(floatvect) atoms.set_velocities(velocities) if len(fix): atoms.set_constraint([FixAtoms(indices=fix)] + fix_cart) else: atoms.set_constraint(fix_cart) images.append(atoms) # if we enter here, the SocketIO/PIMD Wrapper was used elif ("Atomic structure that " "was used in the preceding time step of the wrapper") in line: # parse atoms and add calculator information, i.e., the energies # and forces that were already collected atoms = _parse_atoms(fd, n_atoms=n_atoms) results = images[-1].calc.results atoms.calc = SinglePointCalculator(atoms, **results) # replace last image with updated atoms images[-1] = atoms # make sure `atoms` does not point to `images[-1` later on atoms = atoms.copy() # FlK: add analytical stress and replace stress=None if "Analytical stress tensor - Symmetrized" in line: # scroll to significant lines for _ in range(4): next(fd) stress = [] for _ in range(3): inp = next(fd) stress.append([float(i) for i in inp.split()[2:5]]) if "Total atomic forces" in line: f = [] for i in range(n_atoms): inp = next(fd).split() # FlK: use inp[-3:] instead of inp[1:4] to make sure this works # when atom number is not preceded by a space. f.append([float(i) for i in inp[-3:]]) if not found_aims_calculator: e = images[-1].get_potential_energy() # FlK: Add the stress if it has been computed if stress is None: calc = SinglePointCalculator(atoms, energy=e, forces=f) else: calc = SinglePointCalculator(atoms, energy=e, forces=f, stress=stress) images[-1].calc = calc e = None f = None if "Total energy corrected" in line: e = float(line.split()[5]) if pbc: atoms.set_cell(cell) atoms.pbc = True if not found_aims_calculator: atoms.calc = SinglePointCalculator(atoms, energy=e) if not molecular_dynamics: if len(fix): atoms.set_constraint([FixAtoms(indices=fix)] + fix_cart) else: atoms.set_constraint(fix_cart) images.append(atoms) e = None # if found_aims_calculator: # calc.set_results(images[-1]) # images[-1].calc = calc # FlK: add stress per atom if "Per atom stress (eV) used for heat flux calculation" in line: # scroll to boundary next(l for l in fd if "-------------" in l) stresses = [] for l in [next(fd) for _ in range(n_atoms)]: # Read stresses xx, yy, zz, xy, xz, yz = [float(d) for d in l.split()[2:8]] stresses.append([[xx, xy, xz], [xy, yy, yz], [xz, yz, zz]]) if not found_aims_calculator: e = images[-1].get_potential_energy() f = images[-1].get_forces() stress = images[-1].get_stress(voigt=False) calc = SinglePointCalculator(atoms, energy=e, forces=f, stress=stress, stresses=stresses) images[-1].calc = calc fd.close() if molecular_dynamics: images = images[1:] # return requested images, code borrowed from ase/io/trajectory.py if isinstance(index, int): return images[index] else: step = index.step or 1 if step > 0: start = index.start or 0 if start < 0: start += len(images) stop = index.stop or len(images) if stop < 0: stop += len(images) else: if index.start is None: start = len(images) - 1 else: start = index.start if start < 0: start += len(images) if index.stop is None: stop = -1 else: stop = index.stop if stop < 0: stop += len(images) return [images[i] for i in range(start, stop, step)]
# Benzene on the slab from jasp import * from ase.lattice.surface import fcc111, add_adsorbate from ase.structure import molecule from ase.constraints import FixAtoms atoms = fcc111('Au', size=(3,3,3), vacuum=10) benzene = molecule('C6H6') benzene.translate(-benzene.get_center_of_mass()) # I want the benzene centered on the position in the middle of atoms # 20, 22, 23 and 25 p = (atoms.positions[20] + atoms.positions[22] + atoms.positions[23] + atoms.positions[25])/4.0 + [0.0, 0.0, 3.05] benzene.translate(p) atoms += benzene # now we constrain the slab c = FixAtoms(mask=[atom.symbol=='Au' for atom in atoms]) atoms.set_constraint(c) #from ase.visualize import view; view(atoms) with jasp('surfaces/Au-benzene-pbe', xc='PBE', encut=350, kpts=(4,4,1), ibrion=1, nsw=100, atoms=atoms) as calc: print atoms.get_potential_energy()
def read_aims(filename, apply_constraints=True): """Import FHI-aims geometry type files. Reads unitcell, atom positions and constraints from a geometry.in file. If geometric constraint (symmetry parameters) are in the file include that information in atoms.info["symmetry_block"] """ from ase import Atoms from ase.constraints import ( FixAtoms, FixCartesian, FixScaledParametricRelations, FixCartesianParametricRelations, ) import numpy as np atoms = Atoms() with open(filename, "r") as fd: lines = fd.readlines() positions = [] cell = [] symbols = [] velocities = [] magmoms = [] symmetry_block = [] charges = [] fix = [] fix_cart = [] xyz = np.array([0, 0, 0]) i = -1 n_periodic = -1 periodic = np.array([False, False, False]) cart_positions, scaled_positions = False, False for line in lines: inp = line.split() if inp == []: continue if inp[0] == "atom": cart_positions = True if xyz.all(): fix.append(i) elif xyz.any(): fix_cart.append(FixCartesian(i, xyz)) floatvect = float(inp[1]), float(inp[2]), float(inp[3]) positions.append(floatvect) magmoms.append(0.0) charges.append(0.0) symbols.append(inp[-1]) i += 1 xyz = np.array([0, 0, 0]) elif inp[0] == "atom_frac": scaled_positions = True if xyz.all(): fix.append(i) elif xyz.any(): fix_cart.append(FixCartesian(i, xyz)) floatvect = float(inp[1]), float(inp[2]), float(inp[3]) positions.append(floatvect) magmoms.append(0.0) symbols.append(inp[-1]) i += 1 xyz = np.array([0, 0, 0]) elif inp[0] == "lattice_vector": floatvect = float(inp[1]), float(inp[2]), float(inp[3]) cell.append(floatvect) n_periodic = n_periodic + 1 periodic[n_periodic] = True elif inp[0] == "initial_moment": magmoms[-1] = float(inp[1]) elif inp[0] == "initial_charge": charges[-1] = float(inp[1]) elif inp[0] == "constrain_relaxation": if inp[1] == ".true.": fix.append(i) elif inp[1] == "x": xyz[0] = 1 elif inp[1] == "y": xyz[1] = 1 elif inp[1] == "z": xyz[2] = 1 elif inp[0] == "velocity": floatvect = [v_unit * float(l) for l in inp[1:4]] velocities.append(floatvect) elif inp[0] in [ "symmetry_n_params", "symmetry_params", "symmetry_lv", "symmetry_frac", ]: symmetry_block.append(" ".join(inp)) if xyz.all(): fix.append(i) elif xyz.any(): fix_cart.append(FixCartesian(i, xyz)) if cart_positions and scaled_positions: raise Exception("Can't specify atom positions with mixture of " "Cartesian and fractional coordinates") elif scaled_positions and periodic.any(): atoms = Atoms(symbols, scaled_positions=positions, cell=cell, pbc=periodic) else: atoms = Atoms(symbols, positions) if len(velocities) > 0: if len(velocities) != len(positions): raise Exception( "Number of positions and velocities have to coincide.") atoms.set_velocities(velocities) fix_params = [] if len(symmetry_block) > 5: params = symmetry_block[1].split()[1:] lattice_expressions = [] lattice_params = [] atomic_expressions = [] atomic_params = [] n_lat_param = int(symmetry_block[0].split(" ")[2]) lattice_params = params[:n_lat_param] atomic_params = params[n_lat_param:] for ll, line in enumerate(symmetry_block[2:]): expression = " ".join(line.split(" ")[1:]) if ll < 3: lattice_expressions += expression.split(",") else: atomic_expressions += expression.split(",") fix_params.append( FixCartesianParametricRelations.from_expressions( list(range(3)), lattice_params, lattice_expressions, use_cell=True, )) fix_params.append( FixScaledParametricRelations.from_expressions( list(range(len(atoms))), atomic_params, atomic_expressions)) if any(magmoms): atoms.set_initial_magnetic_moments(magmoms) if any(charges): atoms.set_initial_charges(charges) if periodic.any(): atoms.set_cell(cell) atoms.set_pbc(periodic) if len(fix): atoms.set_constraint([FixAtoms(indices=fix)] + fix_cart + fix_params) else: atoms.set_constraint(fix_cart + fix_params) if fix_params and apply_constraints: atoms.set_positions(atoms.get_positions()) return atoms
b = a / sqrt(2) h = b / 2 initial = Atoms('Al2', positions=[(0, 0, 0), (a / 2, b / 2, -h)], cell=(a, b, 2 * h), pbc=(1, 1, 0)) initial *= (2, 2, 2) initial.append(Atom('Al', (a / 2, b / 2, 3 * h))) initial.center(vacuum=4.0, axis=2) N = len(initial) # number of atoms # Make a mask of zeros and ones that select fixed atoms - the two # bottom layers: mask = initial.positions[:, 2] - min(initial.positions[:, 2]) < 1.5 * h constraint = FixAtoms(mask=mask) initial.set_constraint(constraint) # Calculate using EMT: initial.calc = EMT() # Relax the initial state: QuasiNewton(initial).run(fmax=0.05) e0 = initial.get_potential_energy() traj = Trajectory('dimer_along.traj', 'w', initial) traj.write() # Making dimer mask list: d_mask = [False] * (N - 1) + [True]
def get_atoms(structure, **kwargs): """ Returns ASE Atoms object from pymatgen structure or molecule. Args: structure: pymatgen.core.structure.Structure or pymatgen.core.structure.Molecule **kwargs: other keyword args to pass into the ASE Atoms constructor Returns: ASE Atoms object """ if not structure.is_ordered: raise ValueError("ASE Atoms only supports ordered structures") if not ase_loaded: raise ImportError( "AseAtomsAdaptor requires the ASE package.\nUse `pip install ase` or `conda install ase -c conda-forge`" ) # Construct the base ASE Atoms object symbols = [str(site.specie.symbol) for site in structure] positions = [site.coords for site in structure] if hasattr(structure, "lattice"): cell = structure.lattice.matrix pbc = True else: cell = None pbc = None atoms = Atoms(symbols=symbols, positions=positions, pbc=pbc, cell=cell, **kwargs) # Set the site magmoms in the ASE Atoms object # Note: ASE distinguishes between initial and converged # magnetic moment site properties, whereas pymatgen does not. Therefore, we # have to distinguish between these two when constructing the Structure/Molecule. # The mapping selected here is: # ASE initial magmom <--> Pymatgen "magmom" # ASE final magmom <--> Pymatgen "final_magmom" # ASE initial charge <--> Pymatgen "charge" # ASE final charge <--> Pymatgen "final_charge" if "magmom" in structure.site_properties: initial_magmoms = structure.site_properties["magmom"] atoms.set_initial_magnetic_moments(initial_magmoms) if "charge" in structure.site_properties: initial_charges = structure.site_properties["charge"] atoms.set_initial_charges(initial_charges) if "final_magmom" in structure.site_properties: magmoms = structure.site_properties["final_magmom"] else: magmoms = None if "final_charge" in structure.site_properties: charges = structure.site_properties["final_charge"] else: charges = None if magmoms or charges: if magmoms and charges: calc = SinglePointDFTCalculator( atoms, **{ "magmoms": magmoms, "charges": charges }) elif magmoms: calc = SinglePointDFTCalculator(atoms, **{"magmoms": magmoms}) elif charges: calc = SinglePointDFTCalculator(atoms, **{"charges": charges}) atoms.calc = calc # Get the oxidation states from the structure oxi_states = [ getattr(site.specie, "oxi_state", None) for site in structure ] # Read in selective dynamics if present. Note that the ASE FixAtoms class fixes (x,y,z), so # here we make sure that [False, False, False] or [True, True, True] is set for the site selective # dynamics property. As a consequence, if only a subset of dimensions are fixed, this won't get passed to ASE. if "selective_dynamics" in structure.site_properties: fix_atoms = [] for site in structure: site_prop = site.properties["selective_dynamics"] if site_prop not in [[True, True, True], [False, False, False]]: raise ValueError( "ASE FixAtoms constraint does not support selective dynamics in only some dimensions." "Remove the selective dynamics and try again if you do not need them." ) is_fixed = bool(~np.all(site.properties["selective_dynamics"])) fix_atoms.append(is_fixed) else: fix_atoms = None # Set the selective dynamics with the FixAtoms class. if fix_atoms is not None: atoms.set_constraint(FixAtoms(mask=fix_atoms)) # Add any remaining site properties to the ASE Atoms object for prop in structure.site_properties: if prop not in [ "magmom", "charge", "final_magmom", "final_charge", "selective_dynamics" ]: atoms.set_array(prop, np.array(structure.site_properties[prop])) if np.any(oxi_states): atoms.set_array("oxi_states", np.array(oxi_states)) return atoms