def calculate(self, atoms=None, properties=['energy'], system_changes=all_changes): bad = [ change for change in system_changes if change not in self.supported_changes ] if self.calculator_initialized and any(bad): raise PropertyNotImplementedError( 'Cannot change {} through IPI protocol. ' 'Please create new socket calculator.'.format( bad if len(bad) > 1 else bad[0])) self.calculator_initialized = True if self.server is None: assert self.calc is not None cmd = self.calc.command.replace('PREFIX', self.calc.prefix) self.calc.write_input(atoms, properties=properties, system_changes=system_changes) self.launch_server(cmd) self.atoms = atoms.copy() results = self.server.calculate(atoms) virial = results.pop('virial') if self.atoms.number_of_lattice_vectors == 3 and any(self.atoms.pbc): from ase.constraints import full_3x3_to_voigt_6_stress vol = atoms.get_volume() results['stress'] = -full_3x3_to_voigt_6_stress(virial) / vol self.results.update(results)
def adjust_stress(self, atoms, stress): # symmetrize stress as rank 2 tensor raw_stress = voigt_6_to_full_3x3_stress(stress) symmetrized_stress = symmetrize_rank2(atoms.get_cell(), atoms.get_reciprocal_cell().T, raw_stress, self.rotations) stress[:] = full_3x3_to_voigt_6_stress(symmetrized_stress)
def get_forces(self, apply_constraint=False): atoms_forces = self.atoms.get_forces() # Now, adjust forces: constraint_forces = -atoms_forces old = self.atoms.get_positions() oldcell = self.atoms.get_cell() masses = self.atoms.get_masses() for i in range(self.maxiter): converged = True for j, ab in enumerate(self.pairs): a = ab[0] b = ab[1] cd = self.bondlengths[j] d = old[a] - old[b] d = find_mic([d], oldcell, self.atoms._pbc)[0][0] dv = atoms_forces[a] / masses[a] - atoms_forces[b] / masses[b] m = 1 / (1 / masses[a] + 1 / masses[b]) x = -np.dot(dv, d) / cd**2 if abs(x) > self.tolerance or np.isnan(x) or np.isinf(x): atoms_forces[a] += x * m * d atoms_forces[b] -= x * m * d converged = False if converged: break else: raise RuntimeError('Did not converge') constraint_forces += atoms_forces stress = self.atoms.get_stress() volume = self.atoms.get_volume() virial = -volume * voigt_6_to_full_3x3_stress(stress) atoms_forces = np.dot(atoms_forces, self.deform_grad) dg_inv = np.linalg.inv(self.deform_grad) virial = np.dot(virial, dg_inv.T) if self.hydrostatic_strain: vtr = virial.trace() virial = np.diag([vtr / 3.0, vtr / 3.0, vtr / 3.0]) # Zero out components corresponding to fixed lattice elements if (self.mask != 1.0).any(): virial *= self.mask if self.constant_volume: vtr = virial.trace() np.fill_diagonal(virial, np.diag(virial) - vtr / 3.0) natoms = len(self.atoms) forces = np.zeros((natoms + 3, 3)) forces[:natoms] = atoms_forces forces[natoms:] = virial / self.cell_factor self.stress = -full_3x3_to_voigt_6_stress(virial) / volume return forces
def calculate(self, atoms, properties, system_changes): Calculator.calculate(self, atoms, properties, system_changes) julia_atoms = ASEAtoms(atoms) julia_atoms = convert(julia_atoms) self.results = {} if 'energy' in properties: self.results['energy'] = energy(self.julip_calculator, julia_atoms) if 'forces' in properties: self.results['forces'] = np.array( forces(self.julip_calculator, julia_atoms)) if 'stress' in properties: voigt_stress = full_3x3_to_voigt_6_stress( np.array(stress(self.julip_calculator, julia_atoms))) self.results['stress'] = voigt_stress
def test_stress(): # build a water dimer, which has 6 atoms water1 = molecule('H2O') water2 = molecule('H2O') water2.positions[:, 0] += 5.0 atoms = water1 + water2 atoms.cell = [10, 10, 10] atoms.pbc = True atoms.new_array('stress', np.arange(6, dtype=float)) # array with clashing name atoms.calc = EMT() a_stress = atoms.get_stress() atoms.write('tmp.xyz') b = ase.io.read('tmp.xyz') assert abs(b.get_stress() - a_stress).max() < 1e-6 assert abs(b.arrays['stress'] - np.arange(6, dtype=float)).max() < 1e-6 b_stress = b.info['stress'] assert abs(full_3x3_to_voigt_6_stress(b_stress) - a_stress).max() < 1e-6
def calculate(self, atoms, properties, system_changes): Calculator.calculate(self, atoms, properties, system_changes) if system_changes: self.results = {} if 'energy' in properties and 'energy' not in self.results: self.results['energy'] = self.calc.get_potential_energy(atoms) self.results['free_energy'] = self.results['energy'] if 'forces' in properties and 'forces' not in self.results: raw_forces = self.calc.get_forces(atoms) self.results['forces'] = forces(atoms.get_cell(), atoms.get_reciprocal_cell().T, raw_forces, self.rotations, self.translations, self.symm_map) if 'stress' in properties and 'stress' not in self.results: raw_stress = self.calc.get_stress(atoms) print(raw_stress) if len(raw_stress) == 6: # Voigt raw_stress = voigt_6_to_full_3x3_stress(raw_stress) symmetrized_stress = stress(atoms.get_cell(), atoms.get_reciprocal_cell().T, raw_stress, self.rotations) self.results['stress'] = full_3x3_to_voigt_6_stress(symmetrized_stress)
def calculate(self, atoms=None, properties=['energy'], system_changes=all_changes): bad = [change for change in system_changes if change not in self.supported_changes] if self.calculator_initialized and any(bad): raise PropertyNotImplementedError( 'Cannot change {} through IPI protocol. ' 'Please create new socket calculator.' .format(bad if len(bad) > 1 else bad[0])) self.calculator_initialized = True if self.server is None: #if 'vc' in self.calculation: # self.cell_factor = 10 #self.calculation = 'scf' self.cell_dynamics = 'ipi' self.ion_dynamics = 'ipi' self.dontcalcforces = False self.write_input(atoms, properties=properties, system_changes=system_changes) if self._socket_type=='UNIX' or ((not self._unixsocket and not self._port) and self._socket_type!='INET'): self._socket_type = 'UNIX' if not self._unixsocket: self._unixsocket = self.scratch.split('/')[-1] socket_string = ' --ipi {0}:UNIX >> {1}'.format(self._unixsocket,self.log) elif self._socket_type=='INET' or (not self._unixsocket and self._port): self._socket_type = 'INET' port = SocketServer.default_port SocketServer.default_port += 1 while socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect_ex(('localhost', port)) == 0: port += 1 self._port = port if re.match('^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$', self.ionode_address): self._ip = self.ionode_address elif self.ionode_address in self.site.nic_inet_ips.keys(): self._ip = self.site.nic_inet_ips[self.ionode_address] else: raise Exception('Not a valida IPV4 address or NIC interface: {}'.format(self.ionode_address)) socket_string = ' --ipi {0}:{1} >> {2}'.format(self._ip,self._port,self.log) else: raise Exception('Socket type: {} not implemented.'.format(self._socket_type)) if self.socket_log: print(self._socket_type,file=self.socket_log) cmd = ' '.join(self.command) + socket_string self.launch_server(cmd) results = self.server.calculate(atoms,properties) if 'virial' in results.keys(): if self.atoms.number_of_lattice_vectors == 3 and any(self.atoms.pbc): from ase.constraints import full_3x3_to_voigt_6_stress vol = atoms.get_volume() results['stress'] = -full_3x3_to_voigt_6_stress(results['virial']) / vol else: raise Exception('Stress calculation: cell and periodic boundary conditions must be defined.') self.results.update(results)
def calculate( self, atoms=None, properties=None, system_changes=all_changes, ): if properties is None: properties = self.implemented_properties Calculator.calculate(self, atoms, properties, system_changes) natoms = len(self.atoms) sigma = self.parameters.sigma epsilon = self.parameters.epsilon rc = self.parameters.rc if self.nl is None or 'numbers' in system_changes: self.nl = NeighborList([rc / 2] * natoms, self_interaction=False) self.nl.update(self.atoms) positions = self.atoms.positions cell = self.atoms.cell # potential value at rc e0 = 4 * epsilon * ((sigma / rc)**12 - (sigma / rc)**6) energies = np.zeros(natoms) forces = np.zeros((natoms, 3)) stresses = np.zeros((natoms, 3, 3)) for ii in range(natoms): neighbors, offsets = self.nl.get_neighbors(ii) cells = np.dot(offsets, cell) # pointing *towards* neighbours distance_vectors = positions[neighbors] + cells - positions[ii] r2 = (distance_vectors**2).sum(1) c6 = (sigma**2 / r2)**3 c6[r2 > rc**2] = 0.0 c12 = c6**2 pairwise_energies = 4 * epsilon * (c12 - c6) - e0 * (c6 != 0.0) energies[ii] += 0.5 * pairwise_energies.sum() # atomic energies pairwise_forces = (-24 * epsilon * (2 * c12 - c6) / r2)[:, np.newaxis] * distance_vectors forces[ii] += pairwise_forces.sum(axis=0) stresses[ii] += 0.5 * np.dot( pairwise_forces.T, distance_vectors) # equivalent to outer product # add j < i contributions for jj, atom_j in enumerate(neighbors): energies[atom_j] += 0.5 * pairwise_energies[jj] forces[atom_j] += -pairwise_forces[jj] # f_ji = - f_ij stresses[atom_j] += 0.5 * np.outer(pairwise_forces[jj], distance_vectors[jj]) # no lattice, no stress if self.atoms.number_of_lattice_vectors == 3: stresses = full_3x3_to_voigt_6_stress(stresses) self.results['stress'] = (stresses.sum(axis=0) / self.atoms.get_volume()) self.results['stresses'] = stresses / self.atoms.get_volume() energy = energies.sum() self.results['energy'] = energy self.results['energies'] = energies self.results['free_energy'] = energy self.results['forces'] = forces
def calculate( self, atoms=None, properties=None, system_changes=all_changes, ): if properties is None: properties = self.implemented_properties Calculator.calculate(self, atoms, properties, system_changes) natoms = len(self.atoms) sigma = self.parameters.sigma epsilon = self.parameters.epsilon rc = self.parameters.rc ro = self.parameters.ro smooth = self.parameters.smooth if self.nl is None or 'numbers' in system_changes: self.nl = NeighborList([rc / 2] * natoms, self_interaction=False, bothways=True) self.nl.update(self.atoms) positions = self.atoms.positions cell = self.atoms.cell # potential value at rc e0 = 4 * epsilon * ((sigma / rc)**12 - (sigma / rc)**6) energies = np.zeros(natoms) forces = np.zeros((natoms, 3)) stresses = np.zeros((natoms, 3, 3)) for ii in range(natoms): neighbors, offsets = self.nl.get_neighbors(ii) cells = np.dot(offsets, cell) # pointing *towards* neighbours distance_vectors = positions[neighbors] + cells - positions[ii] r2 = (distance_vectors**2).sum(1) c6 = (sigma**2 / r2)**3 c6[r2 > rc**2] = 0.0 c12 = c6**2 if smooth: cutoff_fn = cutoff_function(r2, rc**2, ro**2) d_cutoff_fn = d_cutoff_function(r2, rc**2, ro**2) pairwise_energies = 4 * epsilon * (c12 - c6) pairwise_forces = -24 * epsilon * (2 * c12 - c6) / r2 # du_ij if smooth: # order matters, otherwise the pairwise energy is already modified pairwise_forces = (cutoff_fn * pairwise_forces + 2 * d_cutoff_fn * pairwise_energies) pairwise_energies *= cutoff_fn else: pairwise_energies -= e0 * (c6 != 0.0) pairwise_forces = pairwise_forces[:, np.newaxis] * distance_vectors energies[ii] += 0.5 * pairwise_energies.sum() # atomic energies forces[ii] += pairwise_forces.sum(axis=0) stresses[ii] += 0.5 * np.dot( pairwise_forces.T, distance_vectors) # equivalent to outer product # no lattice, no stress if self.atoms.cell.rank == 3: stresses = full_3x3_to_voigt_6_stress(stresses) self.results['stress'] = stresses.sum( axis=0) / self.atoms.get_volume() self.results['stresses'] = stresses / self.atoms.get_volume() energy = energies.sum() self.results['energy'] = energy self.results['energies'] = energies self.results['free_energy'] = energy self.results['forces'] = forces