def to_atom_list(self, atoms=None): """ Return list of atom indices that this Table represents. Indices returns are 0-based or 1-based depending on value of :func:`~quippy.get_fortran_indexing`. If `atoms` is not present, the Atoms object passed to Table.from_atom_indices is used, or an exception is raised if this Table was not created in that way. """ if atoms is None: if not hasattr(self, 'atoms'): raise AttributeError( 'Table missing "atoms" attribute, probably' + ' not created by Table.from_atom_indices()') atoms = self.atoms() if atoms is None: raise ValueError('weakref to Table.atoms has expired') if get_fortran_indexing(): first_column = 1 else: first_column = 0 indices = self.int[first_column, :].copy() if not get_fortran_indexing(): indices[:] -= 1 return list(set(list(indices))) # remove duplicates
def __getitem__(self, i): if not self.initialised: if self is self.parent.hysteretic_connect: self.calc_connect_hysteretic(self.parent) else: self.calc_connect(self.parent) distance = farray(0.0) diff = fzeros(3) cosines = fzeros(3) shift = fzeros(3, dtype=np.int32) res = [] if not get_fortran_indexing(): i = i + 1 # convert to 1-based indexing for n in frange(self.n_neighbours(i)): j = self.neighbour(self.parent, i, n, distance, diff, cosines, shift) if not get_fortran_indexing(): j = j - 1 res.append(NeighbourInfo(j, distance, diff, cosines, shift)) if get_fortran_indexing(): res = farray(res) # to give 1-based indexing return res
def from_atom_indices(cls, atoms, mask=None, list=None, force_fortran_indexing=True): """ Construct a new Table containing atomic indices from a list or mask The new table will include 4 integer columns, for indices plus shifts, and is suitable for passing to bfs_step, construct_hysteretic_region, etc. If force_fortran_indexing is True (the default), all atom indices are converted to Fortran 1-based indexing. """ orig_fortran_indexing = get_fortran_indexing() set_fortran_indexing(force_fortran_indexing) try: if mask is None and list is None: raise ValueError('Either mask or list must be present.') if list is not None and force_fortran_indexing and not orig_fortran_indexing: # we were given 0-based atom indices, convert to 1-based indices list = np.array(list) + 1 if mask is not None: if len(mask) != len(atoms): raise ValueError('size of mask must equal number of atoms') mask = mask.astype(bool) # use indexing style given by force_fortran_indexing list = atoms.indices[mask] self = cls(4, 0, 0, 0) self.append(blank_rows=len(list)) self.int[:, :] = 0 if get_fortran_indexing(): first_column = 1 else: first_column = 0 self.int[first_column, :] = list finally: set_fortran_indexing(orig_fortran_indexing) self.atoms = weakref.ref(atoms) return self
def get_atom(self, i): """Return a dictionary containing the properties of the atom with index `i`. If fortran_indexing=True (the default), `i` should be in range 1..self.n, otherwise it should be in range 0..(self.n-1).""" if (get_fortran_indexing() and (i < 1 or i > self.n)) or \ (not get_fortran_indexing() and (i < 0 or i > self.n-1)): raise IndexError('Atoms index out of range') atom = {} atom['_index'] = i atom['atoms'] = self for k in self.properties.keys(): v = self.properties[k][..., i] if isinstance(v, np.ndarray): if v.dtype.kind == 'S': v = ''.join(v).strip() elif v.shape == (): v = v.item() atom[k.lower()] = v return atom
def get_visible(self): """Return list of indices of atoms currently visible in this viewer.""" indices = self._atomeye.get_visible() at = self.gcat() if np.any(indices > len(at)): # atoms duplicated due to narrow cell (n->small_cell_err_handler == 1) indices = list(set([idx % len(at) for idx in indices ])) if get_fortran_indexing(): indices = [idx+1 for idx in indices] return indices
def get_visible(self): """Return list of indices of atoms currently visible in this viewer.""" indices = self._atomeye.get_visible() at = self.gcat() if np.any(indices > len(at)): # atoms duplicated due to narrow cell (n->small_cell_err_handler == 1) indices = list(set([idx % len(at) for idx in indices])) if get_fortran_indexing(): indices = [idx + 1 for idx in indices] return indices
def _get_array_shape(self, name): if name in ('int', 'real', 'logical'): if get_fortran_indexing(): return (slice(None), slice(1, self.n)) else: return (slice(None), slice(0, self.n)) elif name == 'str': return (slice(None), slice(None), slice(1, self.n)) else: return None
def _indices(self): """Return array of atoms indices If global ``fortran_indexing`` is True, returns FortranArray containing numbers 1..self.n. Otherwise, returns a standard numpuy array containing numbers in range 0..(self.n-1).""" if get_fortran_indexing(): return farray(list(frange(len(self)))) else: return np.array(list(range(len(self))))
def get_array(self, key): "Return a _reference_ to an array stored in this Dictionary""" import _quippy, arraydata if key in self and self.get_type_and_size(key)[0] in Dictionary._array_types: a = arraydata.get_array(self._fpointer, _quippy.qp_dictionary_get_array, key) if get_fortran_indexing(): a = FortranArray(a, parent=self) return a else: raise KeyError('Key "%s" does not correspond to an array entry' % key)
def on_click(mod, iw, idx): if (mod,iw) not in viewers: raise RuntimeError('Unexpected module id %d or window id %d' % (mod, iw)) self = viewers[(mod,iw)] at = self.gcat() if at is None: return if idx >= len(at): idx = idx % len(at) if get_fortran_indexing(): idx = idx + 1 # atomeye uses zero based indices self._click_hook(at, idx)
def on_click(mod, iw, idx): if (mod, iw) not in viewers: raise RuntimeError('Unexpected module id %d or window id %d' % (mod, iw)) self = viewers[(mod, iw)] at = self.gcat() if at is None: return if idx >= len(at): idx = idx % len(at) if get_fortran_indexing(): idx = idx + 1 # atomeye uses zero based indices self._click_hook(at, idx)
def __getattr__(self, name): if self.netcdf_file is not None: try: return self.netcdf_file.__getattr__(name) except AttributeError: try: a = self.netcdf_file.variables[name][:] if get_fortran_indexing(): a = farray(a) return a except KeyError: raise AttributeError('Attribute %s not found' % name) else: raise AttributeError('Attribute %s not found' % name)
def to_atom_mask(self, atoms=None): """ Return mask for atom indices that this Table represents Result is either an array of an FortranArray, depending on value of :func:`~quippy.get_fortran_indexing`. If `atoms` is not present, the Atoms object passed to Table.from_atom_indices is used, or an exception is raised if this Table was not created in that way. """ mask = np.zeros(len(atoms), dtype=bool) if get_fortran_indexing(): mask = mask.view(FortranArray) mask[self.to_atom_list(atoms)] = True return mask
def on_redraw(mod, iw): if (mod, iw) not in viewers: raise RuntimeError('Unexpected window id %d' % iw) self = viewers[(mod,iw)] # keep a reference to old atoms around so memory doesn't get free'd prematurely self._previous_atoms = self._current_atoms if self._previous_atoms is not None: self._exit_hook(self._previous_atoms) self._current_atoms = None if self.atoms is None: title = '(null)' n_atom = 0 cell = None arrays = None redraw = 0 else: name = 'Atoms' if hasattr(self._current_atoms, 'filename') and self._current_atoms.filename is not None: name = self._current_atoms.filename if hasattr(self._current_atoms, 'name') and self._current_atoms.name is not None: name = self._current_atoms.name self._current_atoms = self.gcat(update=True) if hasattr(self.atoms, '__iter__'): fmt = "%%0%dd" % ceil(log10(len(self.atoms)+1)) title = '%s frame %s length %s' % (name, fmt % self._frame, fmt % len(self.atoms)) else: title = name self._enter_hook(self._current_atoms) n_atom = len(self._current_atoms) cell = self._current_atoms.get_cell() pbc = self._current_atoms.get_pbc() pos = self._current_atoms.positions for i, p in enumerate(pbc): if not p: cell[i,i] = max(cell[i,i], max(1.0, 2*(pos[:,i].max()-pos[:,i].min()))) try: arrays = self._current_atoms.properties except AttributeError: arrays = {} for key,value in self._current_atoms.arrays.iteritems(): arrays[name_map.get(key,key)] = value redraw = 1 # FIXME, we should decide if we really have to redraw here if redraw and self.verbose: print '-'*len(title) print title print '-'*len(title) print 'Number of atoms: %d' % n_atom print 'Fortran indexing: %r' % get_fortran_indexing() print 'Unit cell:' print cell self._redraw_hook(self._current_atoms) print '\n' sys.stdout.flush() return (redraw, title, n_atom, cell, arrays)
def calculate(self, atoms, properties, system_changes): Calculator.calculate(self, atoms, properties, system_changes) # we will do the calculation in place, to minimise number of copies, # unless atoms is not a quippy Atoms if isinstance(atoms, Atoms): self.quippy_atoms = weakref.proxy(atoms) else: potlog.debug( 'Potential atoms is not quippy.Atoms instance, copy forced!') self.quippy_atoms = Atoms(atoms) initial_arrays = self.quippy_atoms.arrays.keys() initial_info = self.quippy_atoms.info.keys() if properties is None: properties = ['energy', 'forces', 'stress'] # Add any default properties properties = set(self.get_default_properties() + properties) if len(properties) == 0: raise RuntimeError('Nothing to calculate') if not self.calculation_required(atoms, properties): return args_map = { 'energy': { 'energy': None }, 'energies': { 'local_energy': None }, 'forces': { 'force': None }, 'stress': { 'virial': None }, 'numeric_forces': { 'force': 'numeric_force', 'force_using_fd': True, 'force_fd_delta': 1.0e-5 }, 'stresses': { 'local_virial': None }, 'elastic_constants': {}, 'unrelaxed_elastic_constants': {} } # list of properties that require a call to Potential.calc() calc_properties = [ 'energy', 'energies', 'forces', 'numeric_forces', 'stress', 'stresses' ] # list of other properties we know how to calculate other_properties = ['elastic_constants', 'unrelaxed_elastic_constants'] calc_args = {} calc_required = False for property in properties: if property in calc_properties: calc_required = True calc_args.update(args_map[property]) elif property not in other_properties: raise RuntimeError( "Don't know how to calculate property '%s'" % property) if calc_required: self.calc(self.quippy_atoms, args_str=dict_to_args_str(calc_args)) if 'energy' in properties: self.results['energy'] = float(self.quippy_atoms.energy) if 'energies' in properties: self.results['energies'] = self.quippy_atoms.local_energy.copy( ).view(np.ndarray) if 'forces' in properties: self.results['forces'] = self.quippy_atoms.force.copy().view( np.ndarray).T if 'numeric_forces' in properties: self.results[ 'numeric_forces'] = self.quippy_atoms.numeric_force.copy( ).view(np.ndarray).T if 'stress' in properties: stress = -self.quippy_atoms.virial.copy().view( np.ndarray) / self.quippy_atoms.get_volume() # convert to 6-element array in Voigt order self.results['stress'] = np.array([ stress[0, 0], stress[1, 1], stress[2, 2], stress[1, 2], stress[0, 2], stress[0, 1] ]) if 'stresses' in properties: lv = np.array(self.quippy_atoms.local_virial) # make a copy vol_per_atom = self.get( 'vol_per_atom', self.quippy_atoms.get_volume() / len(atoms)) if isinstance(vol_per_atom, basestring): vol_per_atom = self.quippy_atoms.arrays[vol_per_atom] self.results['stresses'] = -lv.T.reshape( (len(atoms), 3, 3), order='F') / vol_per_atom if 'elastic_constants' in properties: cij_dx = self.get('cij_dx', 1e-2) cij = fzeros((6, 6)) self.calc_elastic_constants(self.quippy_atoms, fd=cij_dx, args_str=self.get_calc_args_str(), c=cij, relax_initial=False, return_relaxed=False) if not get_fortran_indexing(): cij = cij.view(np.ndarray) self.results['elastic_constants'] = cij if 'unrelaxed_elastic_constants' in properties: cij_dx = self.get('cij_dx', 1e-2) c0ij = fzeros((6, 6)) self.calc_elastic_constants(self.quippy_atoms, fd=cij_dx, args_str=self.get_calc_args_str(), c0=c0ij, relax_initial=False, return_relaxed=False) if not get_fortran_indexing(): c0ij = c0ij.view(np.ndarray) self.results['unrelaxed_elastic_constants'] = c0ij # copy back any additional output data to results dictionary skip_keys = ['energy', 'force', 'virial', 'numeric_force'] for key in self.quippy_atoms.arrays.keys(): if key not in initial_arrays and key not in skip_keys: self.results[key] = self.quippy_atoms.arrays[key].copy() for key in self.quippy_atoms.info.keys(): if key not in initial_info and key not in skip_keys: if isinstance(self.quippy_atoms.info[key], np.ndarray): self.results[key] = self.quippy_atoms.info[key].copy() else: self.results[key] = self.quippy_atoms.info[key]
def find_atom(self, i): """ Set the anchor to the atom with index `i`. """ if get_fortran_indexing(): i = i-1 self.run_command("find_atom %d" % i)
def find_atom(self, i): """ Set the anchor to the atom with index `i`. """ if get_fortran_indexing(): i = i - 1 self.run_command("find_atom %d" % i)
def on_redraw(mod, iw): if (mod, iw) not in viewers: raise RuntimeError('Unexpected window id %d' % iw) self = viewers[(mod, iw)] # keep a reference to old atoms around so memory doesn't get free'd prematurely self._previous_atoms = self._current_atoms if self._previous_atoms is not None: self._exit_hook(self._previous_atoms) self._current_atoms = None if self.atoms is None: title = '(null)' n_atom = 0 cell = None arrays = None redraw = 0 else: name = 'Atoms' if hasattr( self._current_atoms, 'filename') and self._current_atoms.filename is not None: name = self._current_atoms.filename if hasattr(self._current_atoms, 'name') and self._current_atoms.name is not None: name = self._current_atoms.name self._current_atoms = self.gcat(update=True) if hasattr(self.atoms, '__iter__'): fmt = "%%0%dd" % ceil(log10(len(self.atoms) + 1)) title = '%s frame %s length %s' % (name, fmt % self._frame, fmt % len(self.atoms)) else: title = name self._enter_hook(self._current_atoms) n_atom = len(self._current_atoms) cell = self._current_atoms.get_cell() pbc = self._current_atoms.get_pbc() pos = self._current_atoms.positions for i, p in enumerate(pbc): if not p: cell[i, i] = max( cell[i, i], max(1.0, 2 * (pos[:, i].max() - pos[:, i].min()))) try: arrays = self._current_atoms.properties except AttributeError: arrays = {} for key, value in self._current_atoms.arrays.iteritems(): arrays[name_map.get(key, key)] = value redraw = 1 # FIXME, we should decide if we really have to redraw here if redraw and self.verbose: print '-' * len(title) print title print '-' * len(title) print 'Number of atoms: %d' % n_atom print 'Fortran indexing: %r' % get_fortran_indexing() print 'Unit cell:' print cell self._redraw_hook(self._current_atoms) print '\n' sys.stdout.flush() return (redraw, title, n_atom, cell, arrays)
def calculate(self, atoms, quantities=None): """ Perform a calculation of `quantities` for `atoms` using this Potential. Automatically determines if a new calculation is required or if previous results are still appliciable (i.e. if the atoms haven't moved since last call) Called internally by :meth:`get_potential_energy`, :meth:`get_forces`, etc. """ if quantities is None: quantities = ['energy', 'forces', 'stress'] # Add any default quantities quantities = set(self.get_default_quantities() + quantities) if len(quantities) == 0: raise RuntimeError('Nothing to calculate') if not self.calculation_required(atoms, quantities): return args_map = { 'energy': { 'energy': None }, 'energies': { 'local_energy': None }, 'forces': { 'force': None }, 'stress': { 'virial': None }, 'numeric_forces': { 'force': 'numeric_force', 'force_using_fd': True, 'force_fd_delta': 1.0e-5 }, 'stresses': { 'local_virial': None }, 'elastic_constants': {}, 'unrelaxed_elastic_constants': {} } # list of quantities that require a call to Potential.calc() calc_quantities = [ 'energy', 'energies', 'forces', 'numeric_forces', 'stress', 'stresses' ] # list of other quantities we know how to calculate other_quantities = ['elastic_constants', 'unrelaxed_elastic_constants'] calc_args = {} calc_required = False for quantity in quantities: if quantity in calc_quantities: calc_required = True calc_args.update(args_map[quantity]) elif quantity not in other_quantities: raise RuntimeError( "Don't know how to calculate quantity '%s'" % quantity) if calc_required: self.calc(self.atoms, args_str=dict_to_args_str(calc_args)) if 'energy' in quantities: self.energy = float(self.atoms.energy) if 'energies' in quantities: self.energies = self.atoms.local_energy.view(np.ndarray) if 'forces' in quantities: self.forces = self.atoms.force.view(np.ndarray).T if 'numeric_forces' in quantities: self.numeric_forces = self.atoms.numeric_force.view(np.ndarray).T if 'stress' in quantities: stress = -self.atoms.virial.view( np.ndarray) / self.atoms.get_volume() # convert to 6-element array in Voigt order self.stress = np.array([ stress[0, 0], stress[1, 1], stress[2, 2], stress[1, 2], stress[0, 2], stress[0, 1] ]) if 'stresses' in quantities: lv = np.array(self.atoms.local_virial) # make a copy vol_per_atom = self.get('vol_per_atom', self.atoms.get_volume() / len(atoms)) if isinstance(vol_per_atom, basestring): vol_per_atom = self.atoms.arrays[vol_per_atom] self.stresses = -lv.T.reshape( (len(atoms), 3, 3), order='F') / vol_per_atom if 'elastic_constants' in quantities: cij_dx = self.get('cij_dx', 1e-2) cij = fzeros((6, 6)) self.calc_elastic_constants(self.atoms, fd=cij_dx, args_str=self.get_calc_args_str(), c=cij, relax_initial=False, return_relaxed=False) if not get_fortran_indexing(): cij = cij.view(np.ndarray) self.elastic_constants = cij if 'unrelaxed_elastic_constants' in quantities: cij_dx = self.get('cij_dx', 1e-2) c0ij = fzeros((6, 6)) self.calc_elastic_constants(self.atoms, fd=cij_dx, args_str=self.get_calc_args_str(), c0=c0ij, relax_initial=False, return_relaxed=False) if not get_fortran_indexing(): c0ij = c0ij.view(np.ndarray) self.unrelaxed_elastic_constants = c0ij