def numeric_forces(atoms, indices=None, axes=(0, 1, 2), d=0.001, parallel=None, name=None): """Evaluate finite-difference forces on several atoms. Returns an array of forces for each specified atomic index and each specified axis, calculated using finite difference on each atom and direction separately. Array has same shape as if returned from atoms.get_forces(); uncalculated elements are zero. Calculates all forces by default.""" if indices is None: indices = range(len(atoms)) F_ai = np.zeros_like(atoms.positions) n = len(indices) * len(axes) if parallel is None: atom_tasks = [atoms] * n master = True calc_comm = world else: calc_comm, tasks_comm, tasks_rank = distribute_cpus(parallel, world) master = calc_comm.rank == 0 calculator = atoms.get_calculator() calculator.set(communicator=calc_comm) atom_tasks = [None] * n for i in range(n): if ((i - tasks_rank) % tasks_comm.size) == 0: atom_tasks[i] = atoms for ia, a in enumerate(indices): for ii, i in enumerate(axes): atoms = atom_tasks[ia * len(axes) + ii] if atoms is not None: done = 0 if name: fname = '%s.%d%s.pckl' % (name, a, 'xyz'[i]) fd = opencew(fname, calc_comm) if fd is None: if master: try: F_ai[a, i] = pickle.load(open(fname)) print '# atom', a, 'xyz'[i], 'done' done = 1 except EOFError: pass done = calc_comm.sum(done) if not done: print '# rank', rank, 'calculating atom', a, 'xyz'[i] force = numeric_force(atoms, a, i, d) if master: F_ai[a, i] = force if name: fd = open('%s.%d%s.pckl' % (name, a, 'xyz'[i]), 'w') pickle.dump(force, fd) fd.close() if parallel is not None: world.sum(F_ai) return F_ai
def calculate_energies_and_forces(self, all=False): """Calculate and store the forces and energies for the band.""" images = self.images if all: self.forces['real'][0] = images[0].get_forces() self.forces['real'][-1] = images[-1].get_forces() if not self.integrate_forces: self.energies[0] = images[0].get_potential_energy() self.energies[-1] = images[-1].get_potential_energy() if not self.parallel: # Do all images - one at a time: for i in range(1, self.nimages - 1): self.calculate_image_forces(i) for i in range(1, self.nimages - 1): self.calculate_image_energies(i) else: # Parallelize over images: first the forces ... i = rank * (self.nimages - 2) // size + 1 try: self.calculate_image_forces(i) except: # Make sure other images also fail: error = world.sum(1.0) raise else: error = world.sum(0.0) if error: raise RuntimeError('Parallel NEB failed') for i in range(1, self.nimages - 1): root = (i - 1) * size // (self.nimages - 2) world.broadcast(self.forces['real'][i], root) # ... and now the energies try: self.calculate_image_energies(i) except: # Make sure other images also fail: error = world.sum(1.0) raise else: error = world.sum(0.0) if error: raise RuntimeError('Parallel NEB failed') for i in range(1, self.nimages - 1): root = (i - 1) * size // (self.nimages - 2) world.broadcast(self.energies[i : i + 1], root) if all and self.integrate_forces: # fill in the first and last energies by force integration self.energies[0] = 0. self.calculate_image_energies(len(self.images)-1)
def ltidos(cell, eigs, energies, weights=None): """DOS from linear tetrahedron interpolation. cell: 3x3 ndarray-like Unit cell. eigs: (n1, n2, n3, nbands)-shaped ndarray Eigenvalues on a Monkhorst-Pack grid (not reduced). energies: 1-d array-like Energies where the DOS is calculated (must be a uniform grid). weights: (n1, n2, n3, nbands)-shaped ndarray Weights. Defaults to 1. """ from scipy.spatial import Delaunay I, J, K = size = eigs.shape[:3] B = (np.linalg.inv(cell) / size).T indices = np.array([[i, j, k] for i in [0, 1] for j in [0, 1] for k in [0, 1]]) dt = Delaunay(np.dot(indices, B)) dos = np.zeros_like(energies) integrate = functools.partial(_lti, energies, dos) for s in dt.simplices: kpts = dt.points[s] try: M = np.linalg.inv(kpts[1:, :] - kpts[0, :]) except np.linalg.linalg.LinAlgError: continue n = -1 for i in range(I): for j in range(J): for k in range(K): n += 1 if n % world.size != world.rank: continue E = np.array([ eigs[(i + a) % I, (j + b) % J, (k + c) % K] for a, b, c in indices[s] ]) if weights is None: integrate(kpts, M, E) else: w = np.array([ weights[(i + a) % I, (j + b) % J, (k + c) % K] for a, b, c in indices[s] ]) integrate(kpts, M, E, w) world.sum(dos) return dos * abs(np.linalg.det(cell))
def opencew(filename, world=None): """Create and open filename exclusively for writing. If master cpu gets exclusive write access to filename, a file descriptor is returned (a dummy file descriptor is returned on the slaves). If the master cpu does not get write access, None is returned on all processors.""" if world is None: from ase.parallel import world if world.rank == 0: try: fd = os.open(filename, CEW_FLAGS) except OSError as ex: error = ex.errno else: error = 0 fd = os.fdopen(fd, 'wb') else: error = 0 fd = devnull # Syncronize: error = world.sum(error) if error == errno.EEXIST: return None if error: raise OSError(error, 'Error', filename) return fd
def opencew(filename, world=None): """Create and open filename exclusively for writing. If master cpu gets exclusive write access to filename, a file descriptor is returned (a dummy file descriptor is returned on the slaves). If the master cpu does not get write access, None is returned on all processors.""" if world is None: from ase.parallel import world if world.rank == 0: try: fd = os.open(filename, os.O_CREAT | os.O_EXCL | os.O_WRONLY) except OSError: ok = 0 else: ok = 1 fd = os.fdopen(fd, 'wb') else: ok = 0 fd = devnull # Syncronize: if world.sum(ok) == 0: return None else: return fd
def _opencew(filename, world=None): if world is None: from ase.parallel import world closelater = [] def opener(file, flags): return os.open(file, flags | CEW_FLAGS) try: error = 0 if world.rank == 0: try: fd = open(filename, 'wb', opener=opener) except OSError as ex: error = ex.errno else: closelater.append(fd) else: fd = open(os.devnull, 'wb') closelater.append(fd) # Synchronize: error = world.sum(error) if error == errno.EEXIST: return None if error: raise OSError(error, 'Error', filename) return fd except BaseException: for fd in closelater: fd.close() raise
def opencew(filename, world=None): """Create and open filename exclusively for writing. If master cpu gets exclusive write access to filename, a file descriptor is returned (a dummy file descriptor is returned on the slaves). If the master cpu does not get write access, None is returned on all processors.""" if world is None: from ase.parallel import world if world.rank == 0: try: fd = os.open(filename, os.O_CREAT | os.O_EXCL | os.O_WRONLY) except OSError: ok = 0 else: ok = 1 fd = os.fdopen(fd, 'w') else: ok = 0 fd = devnull # Syncronize: if world.sum(ok) == 0: return None else: return fd
def lti_dos(simplices, eigs, weights, energies, dos, world): shape = eigs.shape[:3] nweights = weights.shape[-1] dos[:] = 0.0 n = -1 for index in np.indices(shape).reshape((3, -1)).T: n += 1 if n % world.size != world.rank: continue i = ((index + simplices) % shape).T E = eigs[i[0], i[1], i[2]].reshape((4, -1)) W = weights[i[0], i[1], i[2]].reshape((4, -1, nweights)) for e, w in zip(E.T, W.transpose((1, 0, 2))): lti_dos1(e, w, energies, dos) dos /= 6.0 world.sum(dos)
def __init__(self, atoms, control=None, eigenmodes=None, random_seed=None, **kwargs): self.minmode_init = True self.atoms = atoms # Initialize to None to avoid strange behaviour due to __getattr__ self.eigenmodes = eigenmodes self.curvatures = None if control is None: self.control = DimerControl(**kwargs) w = 'Missing control object in ' + self.__class__.__name__ + \ '. Using default: DimerControl()' warnings.warn(w, UserWarning) if self.control.logfile is not None: self.control.logfile.write('DIM:WARN: ' + w + '\n') self.control.logfile.flush() else: self.control = control logfile = self.control.get_logfile() mlogfile = self.control.get_eigenmode_logfile() for key in kwargs: if key == 'logfile': logfile = kwargs[key] elif key == 'eigenmode_logfile': mlogfile = kwargs[key] else: self.control.set_parameter(key, kwargs[key]) self.control.initialize_logfiles(logfile=logfile, eigenmode_logfile=mlogfile) # Seed the randomness if random_seed is None: t = time.time() if world.size > 1: t = world.sum(t) / world.size # Harvest the latter part of the current time random_seed = int(('%30.9f' % t)[-9:]) self.random_state = np.random.RandomState(random_seed) # Check the order self.order = self.control.get_parameter('order') # Construct the curvatures list self.curvatures = [100.0] * self.order # Save the original state of the atoms. self.atoms0 = self.atoms.copy() self.save_original_forces() # Get a reference to the log files self.logfile = self.control.get_logfile() self.mlogfile = self.control.get_eigenmode_logfile()
def __init__(self, atoms, control=None, eigenmodes=None, basis=None, random_seed=None, **kwargs): self.minmode_init = True self.atoms = atoms # Initialize to None to avoid strange behaviour due to __getattr__ self.eigenmodes = eigenmodes self.basis = basis self.curvatures = None if control is None: self.control = DimerControl(**kwargs) w = 'Missing control object in ' + self.__class__.__name__ + \ '. Using default: DimerControl()' warnings.warn(w, UserWarning) if self.control.logfile is not None: self.control.logfile.write('DIM:WARN: ' + w + '\n') self.control.logfile.flush() else: self.control = control logfile = self.control.get_logfile() mlogfile = self.control.get_eigenmode_logfile() for key in kwargs: if key == 'logfile': logfile = kwargs[key] elif key == 'eigenmode_logfile': mlogfile = kwargs[key] else: self.control.set_parameter(key, kwargs[key]) self.control.initialize_logfiles(logfile = logfile, eigenmode_logfile = mlogfile) # Print the log header self.control.log_header() # Seed the randomness if random_seed is None: t = time.time() if size > 1: t = world.sum(t) / float(size) # Harvest the latter part of the current time random_seed = int(('%30.9f' % t)[-9:]) self.random_state = np.random.RandomState(random_seed) # Check the order self.order = self.control.get_parameter('order') # Construct the curvatures list self.curvatures = [100.0] * self.order # Save the original state of the atoms. self.atoms0 = self.atoms.copy() self.save_original_forces() # Get a reference to the log files self.logfile = self.control.get_logfile() self.mlogfile = self.control.get_eigenmode_logfile()
def numeric_forces(atoms, indices=None, axes=(0, 1, 2), d=0.001, parallel=None): """Evaluate finite-difference forces on several atoms. Returns an array of forces for each specified atomic index and each specified axis, calculated using finite difference on each atom and direction separately. Array has same shape as if returned from atoms.get_forces(); uncalculated elements are zero. Calculates all forces by default.""" if indices is None: indices = range(len(atoms)) F_ai = np.zeros_like(atoms.positions) n = len(indices) * len(axes) if parallel is None: atom_tasks = [atoms] * n master = True else: calc_comm, tasks_comm, tasks_rank = distribute_cpus(parallel, world) master = calc_comm.rank == 0 calculator = atoms.get_calculator() calculator.set(communicator=calc_comm) atom_tasks = [None] * n for i in range(n): if ((i - tasks_rank) % tasks_comm.size) == 0: atom_tasks[i] = atoms for ia, a in enumerate(indices): for ii, i in enumerate(axes): atoms = atom_tasks[ia * len(axes) + ii] if atoms is not None: print '# rank', rank, 'calculating atom', a, 'xyz'[i] force = numeric_force(atoms, a, i, d) if master: F_ai[a, i] = force if parallel is not None: world.sum(F_ai) return F_ai
def calculate_eigenmodes(self): if self.parallel: i = rank * (self.nimages - 2) // size + 1 try: self.calculate_image_eigenmode(i) except: # Make sure other images also fail: error = world.sum(1.0) raise else: error = world.sum(0.0) if error: raise RuntimeError('Parallel ERM failed during eigenmode calculations.') for i in range(1, self.nimages - 1): if self.images[i].eigenmodes is None: self.images[i].eigenmodes = [np.zeros(self.images[i].get_positions().shape)] root = (i - 1) * size // (self.nimages - 2) world.broadcast(self.images[i].eigenmodes[0], root) # world.broadcast(self.images[i : i + 1].curvatures, root) else: for i in range(1, self.nimages - 1): self.calculate_image_eigenmode(i)
def isfile(filename): """Check if a file is opened, taking into account the MPI environment input: filename output: 0 when the file does not exist, 1 if it does """ if world.rank == 0: isf = os.path.isfile(filename) else: isf = 0 # Synchronize: return world.sum(isf)
def calculate_energies_and_forces(self): images = self.images if not self.parallel: # Do all images - one at a time: for i in range(1, self.nimages - 1): self.calculate_image_energies_and_forces(i) else: # Parallelize over images: i = rank * (self.nimages - 2) // size + 1 try: self.calculate_image_energies_and_forces(i) except: # Make sure other images also fail: error = world.sum(1.0) raise else: error = world.sum(0.0) if error: raise RuntimeError('Parallel NEB failed') for i in range(1, self.nimages - 1): root = (i - 1) * size // (self.nimages - 2) world.broadcast(self.energies[i : i + 1], root) world.broadcast(self.forces['real'][i], root)
def calculate(self, atoms): self.positions = atoms.get_positions().copy() self.cell = atoms.get_cell().copy() self.pbc = atoms.get_pbc().copy() natoms = len(atoms) nH2O = natoms // 3 assert self.pbc.all() C = self.cell.diagonal() assert not (self.cell - np.diag(C)).any() assert (C >= 2 * self.rc2).all() self.numbers = atoms.get_atomic_numbers() Z = self.numbers.reshape((-1, 3)) assert (Z[:, 1:] == 1).all() and (Z[:, 0] == 8).all() R = self.positions.reshape((nH2O, 3, 3)) RO = R[:, 0] self.energy = 0.0 self.forces = np.zeros((natoms, 3)) if world is None: mya = range(nH2O - 1) else: rank = world.rank size = world.size assert nH2O // (2 * size) == 0 mynH2O = nH2O // 2 // size mya = (range(rank * n, (rank + 1) * n) + range((size - rank - 1) * n, (size - rank) * n)) q = np.empty(3) q[:] = qH * (units.Hartree * units.Bohr)**0.5 q[0] *= -2 for a in mya: DOO = (RO[a + 1:] - RO[a] + 0.5 * C) % C - 0.5 * C dOO = (DOO**2).sum(axis=1)**0.5 x1 = dOO > self.rc1 x2 = dOO < self.rc2 f = np.zeros(nH2O - a - 1) f[x2] = 1.0 dfdd = np.zeros(nH2O - a - 1) x12 = np.logical_and(x1, x2) d = (dOO[x12] - self.rc1) / (self.rc2 - self.rc1) f[x12] -= d**2 * (3.0 - 2.0 * d) dfdd[x12] -= 6.0 / (self.rc2 - self.rc1) * d * (1.0 - d) y = (sigma0 / dOO)**6 y2 = y**2 e = 4 * epsilon0 * (y2 - y) self.energy += np.dot(e, f) dedd = 24 * epsilon0 * (2 * y2 - y) / dOO * f - e * dfdd F = (dedd / dOO)[:, np.newaxis] * DOO self.forces[(a + 1) * 3::3] += F self.forces[a * 3] -= F.sum(axis=0) for i in range(3): D = (R[a + 1:] - R[a, i] + 0.5 * C) % C - 0.5 * C d = (D**2).sum(axis=2)**0.5 e = q[i] * q / d self.energy += np.dot(f, e).sum() F = (e / d**2 * f[:, np.newaxis])[:, :, np.newaxis] * D F[:, 0] -= (e.sum(axis=1) * dfdd / dOO)[:, np.newaxis] * DOO self.forces[(a + 1) * 3:] += F.reshape((-1, 3)) self.forces[a * 3 + i] -= F.sum(axis=0).sum(axis=0) if world is not None: self.energy = world.sum(self.energy) world.sum(self.forces)
def calculate(self, atoms): self.positions = atoms.get_positions().copy() self.cell = atoms.get_cell().copy() self.pbc = atoms.get_pbc().copy() natoms = len(atoms) nH2O = natoms // 3 assert self.pbc.all() C = self.cell.diagonal() assert not (self.cell - np.diag(C)).any() assert (C >= 2 * self.rc2).all() self.numbers = atoms.get_atomic_numbers() Z = self.numbers.reshape((-1, 3)) assert (Z[:, 1:] == 1).all() and (Z[:, 0] == 8).all() R = self.positions.reshape((nH2O, 3, 3)) RO = R[:, 0] self.energy = 0.0 self.forces = np.zeros((natoms, 3)) if world.size == 1: mya = list(range(nH2O - 1)) else: rank = world.rank size = world.size assert nH2O // (2 * size) == 0 mynH2O = nH2O // 2 // size mya = (list(range(rank * n, (rank + 1) * n)) + list(range((size - rank - 1) * n, (size - rank) * n))) q = np.empty(3) q[:] = qH * (units.Hartree * units.Bohr)**0.5 q[0] *= -2 for a in mya: DOO = (RO[a + 1:] - RO[a] + 0.5 * C) % C - 0.5 * C dOO = (DOO**2).sum(axis=1)**0.5 x1 = dOO > self.rc1 x2 = dOO < self.rc2 f = np.zeros(nH2O - a - 1) f[x2] = 1.0 dfdd = np.zeros(nH2O - a - 1) x12 = np.logical_and(x1, x2) d = (dOO[x12] - self.rc1) / (self.rc2 - self.rc1) f[x12] -= d**2 * (3.0 - 2.0 * d) dfdd[x12] -= 6.0 / (self.rc2 - self.rc1) * d * (1.0 - d) y = (sigma0 / dOO)**6 y2 = y**2 e = 4 * epsilon0 * (y2 - y) self.energy += np.dot(e, f) dedd = 24 * epsilon0 * (2 * y2 - y) / dOO * f - e * dfdd F = (dedd / dOO)[:, np.newaxis] * DOO self.forces[(a + 1) * 3::3] += F self.forces[a * 3] -= F.sum(axis=0) for i in range(3): D = (R[a + 1:] - R[a, i] + 0.5 * C) % C - 0.5 * C d = (D**2).sum(axis=2)**0.5 e = q[i] * q / d self.energy += np.dot(f, e).sum() F = (e / d**2 * f[:, np.newaxis])[:, :, np.newaxis] * D F[:, 0] -= (e.sum(axis=1) * dfdd / dOO)[:, np.newaxis] * DOO self.forces[(a + 1) * 3:] += F.reshape((-1, 3)) self.forces[a * 3 + i] -= F.sum(axis=0).sum(axis=0) self.energy = world.sum(self.energy) world.sum(self.forces)
def numeric_forces(self,atoms, axes=(0, 1, 2), d=0.001, parallel=None, name=None): """Evaluate finite-difference forces on several atoms. Returns an array of forces for each specified atomic index and each specified axis, calculated using finite difference on each atom and direction separately. Array has same shape as if returned from atoms.get_forces(); uncalculated elements are zero. Calculates all forces by default.""" import numpy as np from ase.parallel import world, rank, distribute_cpus from ase.utils import opencew indices = range(len(atoms)) F_ai = np.zeros_like(atoms.positions) n = len(indices) * len(axes) total_calculation = len(indices)*len(axes) if parallel is None: atom_tasks = [atoms] * n master = True calc_comm = world else: calc_comm, tasks_comm, tasks_rank = distribute_cpus(parallel, world) master = calc_comm.rank == 0 calculator = atoms.get_calculator() calculator.set(communicator=calc_comm) atom_tasks = [None] * n for i in range(n): if ((i - tasks_rank) % tasks_comm.size) == 0: atom_tasks[i] = atoms counter = 0 for ia, a in enumerate(indices): for ii, i in enumerate(axes): atoms = atom_tasks[ia * len(axes) + ii] if atoms is not None: done = 0 if name: fname = '%s.%d%s.pckl' % (name, a, 'xyz'[i]) fd = opencew(fname, calc_comm) if fd is None: if master: try: F_ai[a, i] = pickle.load(open(fname)) print '# atom', a, 'xyz'[i], 'done' done = 1 except EOFError: pass done = calc_comm.sum(done) if not done: # print '# rank', rank, 'calculating atom', a, 'xyz'[i] force = self.__numeric_force(atoms, a, i, d) if master: F_ai[a, i] = force if name: fd = open('%s.%d%s.pckl' % (name, a, 'xyz'[i]), 'w') pickle.dump(force, fd) fd.close() counter += 1 fan = ['-', '\\', '|', '/'] sys.stderr.write("\r[%-100s]%3.1f%% %1s" % tuple([int(float(counter)/float(total_calculation)*100)*'=',float(counter)/float(total_calculation)*100,fan[counter%4]])) sys.stderr.flush() if parallel is not None: world.sum(F_ai) return F_ai
def numeric_forces(self, atoms, axes=(0, 1, 2), d=0.001, parallel=None, name=None): """Evaluate finite-difference forces on several atoms. Returns an array of forces for each specified atomic index and each specified axis, calculated using finite difference on each atom and direction separately. Array has same shape as if returned from atoms.get_forces(); uncalculated elements are zero. Calculates all forces by default.""" import numpy as np from ase.parallel import world, rank, distribute_cpus from ase.utils import opencew indices = range(len(atoms)) F_ai = np.zeros_like(atoms.positions) n = len(indices) * len(axes) total_calculation = len(indices) * len(axes) if parallel is None: atom_tasks = [atoms] * n master = True calc_comm = world else: calc_comm, tasks_comm, tasks_rank = distribute_cpus( parallel, world) master = calc_comm.rank == 0 calculator = atoms.get_calculator() calculator.set(communicator=calc_comm) atom_tasks = [None] * n for i in range(n): if ((i - tasks_rank) % tasks_comm.size) == 0: atom_tasks[i] = atoms counter = 0 for ia, a in enumerate(indices): for ii, i in enumerate(axes): atoms = atom_tasks[ia * len(axes) + ii] if atoms is not None: done = 0 if name: fname = '%s.%d%s.pckl' % (name, a, 'xyz'[i]) fd = opencew(fname, calc_comm) if fd is None: if master: try: F_ai[a, i] = pickle.load(open(fname)) print '# atom', a, 'xyz'[i], 'done' done = 1 except EOFError: pass done = calc_comm.sum(done) if not done: # print '# rank', rank, 'calculating atom', a, 'xyz'[i] force = self.__numeric_force(atoms, a, i, d) if master: F_ai[a, i] = force if name: fd = open('%s.%d%s.pckl' % (name, a, 'xyz'[i]), 'w') pickle.dump(force, fd) fd.close() counter += 1 fan = ['-', '\\', '|', '/'] sys.stderr.write("\r[%-100s]%3.1f%% %1s" % tuple([ int(float(counter) / float(total_calculation) * 100) * '=', float(counter) / float(total_calculation) * 100, fan[counter % 4] ])) sys.stderr.flush() if parallel is not None: world.sum(F_ai) return F_ai
def __len__(self): return world.sum(len(self.backend))