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 ] # First time calculate() is called, system_changes will be # all_changes. After that, only positions and cell may change. if self.atoms is not None 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.atoms = atoms.copy() if self.server is None: self.server = self.launch_server() proc = self.launch_client(atoms, properties, port=self._port, unixsocket=self._unixsocket) self.server.proc = proc # XXX nasty hack results = self.server.calculate(atoms) results['free_energy'] = results['energy'] virial = results.pop('virial') if self.atoms.cell.rank == 3 and any(self.atoms.pbc): 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.cell.reciprocal().T, raw_stress, self.rotations) stress[:] = full_3x3_to_voigt_6_stress(symmetrized_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 # array with clashing name atoms.new_array('stress', np.arange(6, dtype=float)) 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 compute_properties(self) -> Dict: if self.neighbors.did_buffer_overflow: self.update_neighbor_list() properties = self.potential(self.R, neighbor=self.neighbors) ( potential_energy, potential_energies, forces, stress, ) = jax_utils.block_and_dispatch(properties) result = { "energy": potential_energy, "energies": potential_energies, "forces": forces, } if stress is not None: result["stress"] = full_3x3_to_voigt_6_stress(stress) return result
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