Exemple #1
0
    def calc(self,
             at,
             energy=None,
             force=None,
             virial=None,
             local_energy=None,
             local_virial=None,
             args_str=None,
             error=None,
             **kwargs):

        if not isinstance(args_str, basestring):
            args_str = dict_to_args_str(args_str)

        kw_args_str = dict_to_args_str(kwargs)

        args_str = ' '.join((self.get_calc_args_str(), kw_args_str, args_str))

        if isinstance(energy, basestring):
            args_str = args_str + ' energy=%s' % energy
            energy = None
        if isinstance(energy, bool) and energy:
            args_str = args_str + ' energy'
            energy = None

        if isinstance(force, basestring):
            args_str = args_str + ' force=%s' % force
            force = None
        if isinstance(force, bool) and force:
            args_str = args_str + ' force'
            force = None

        if isinstance(virial, basestring):
            args_str = args_str + ' virial=%s' % virial
            virial = None
        if isinstance(virial, bool) and virial:
            args_str = args_str + ' virial'
            virial = None

        if isinstance(local_energy, basestring):
            args_str = args_str + ' local_energy=%s' % local_energy
            local_energy = None
        if isinstance(local_energy, bool) and local_energy:
            args_str = args_str + ' local_energy'
            local_energy = None

        if isinstance(local_virial, basestring):
            args_str = args_str + ' local_virial=%s' % local_virial
            local_virial = None
        if isinstance(local_virial, bool) and local_virial:
            args_str = args_str + ' local_virial'
            local_virial = None

        potlog.debug(
            'Potential invoking calc() on n=%d atoms with args_str "%s"' %
            (len(at), args_str))
        _potential.Potential.calc(self, at, energy, force, virial,
                                  local_energy, local_virial, args_str, error)
Exemple #2
0
 def __init__(self, args_str=None, **init_args):
     """
     Initialises Descriptor object and calculate number of dimensions and
     permutations.
     """
     if args_str is None:
         args_str = dict_to_args_str(init_args)
     RawDescriptor.__init__(self, args_str)
     self._n_dim = self.dimensions()
     self._n_perm = self.n_permutations()
Exemple #3
0
    def calc(self, at, grad=False, args_str=None, **calc_args):
        """
        Calculates all descriptors of this type in the Atoms object, and
        gradients if grad=True. Results can be accessed dictionary- or
        attribute-style; 'descriptor' contains descriptor values, 
        'descriptor_index_0based' contains the 0-based indices of the central 
        atom(s) in each descriptor, 'grad' contains gradients, 
        'grad_index_0based' contains indices to gradients (descriptor, atom).
        Cutoffs and gradients of cutoffs are also returned.
        """
        if args_str is None:
            args_str = dict_to_args_str(calc_args)

        n_index = fzeros(1, 'i')
        n_desc, n_cross = self.descriptor_sizes(at, n_index=n_index)
        n_index = n_index[1]
        data = fzeros((self.n_dim, n_desc))
        cutoff = fzeros(n_desc)
        data_index = fzeros((n_index, n_desc), 'i')

        if grad:
            # n_cross is number of cross-terms, proportional to n_desc
            data_grad = fzeros((self.n_dim, 3, n_cross))
            data_grad_index = fzeros((2, n_cross), 'i')
            cutoff_grad = fzeros((3, n_cross))

        if not grad:
            RawDescriptor.calc(self,
                               at,
                               descriptor_out=data,
                               covariance_cutoff=cutoff,
                               descriptor_index=data_index,
                               args_str=args_str)
        else:
            RawDescriptor.calc(self,
                               at,
                               descriptor_out=data,
                               covariance_cutoff=cutoff,
                               descriptor_index=data_index,
                               grad_descriptor_out=data_grad,
                               grad_descriptor_index=data_grad_index,
                               grad_covariance_cutoff=cutoff_grad,
                               args_str=args_str)

        results = DescriptorCalcResult()
        convert = lambda data: np.array(data).T
        results.descriptor = convert(data)
        results.cutoff = convert(cutoff)
        results.descriptor_index_0based = convert(data_index - 1)
        if grad:
            results.grad = convert(data_grad)
            results.grad_index_0based = convert(data_grad_index - 1)
            results.cutoff_grad = convert(cutoff_grad)

        return results
Exemple #4
0
 def get_calc_args_str(self):
     """
     Get the ``calc_args`` to be passed to :meth:`calc` as a string
     """
     return dict_to_args_str(self._calc_args)
Exemple #5
0
    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]
Exemple #6
0
    def __init__(self,
                 init_args=None,
                 pot1=None,
                 pot2=None,
                 param_str=None,
                 param_filename=None,
                 bulk_scale=None,
                 mpi_obj=None,
                 callback=None,
                 calculator=None,
                 atoms=None,
                 calculation_always_required=False,
                 fpointer=None,
                 finalise=True,
                 error=None,
                 **kwargs):

        self._calc_args = {}
        self._default_properties = []
        self.calculation_always_required = calculation_always_required
        Calculator.__init__(self, atoms=atoms)

        if callback is not None or calculator is not None:
            if init_args is None:
                init_args = 'callbackpot'

        param_dirname = None
        if param_filename is not None:
            param_str = open(param_filename).read()
            param_dirname = path.dirname(param_filename) or None

        if init_args is None and param_str is None:
            raise ValueError('Need one of init_args,param_str,param_filename')

        if init_args is not None:
            if init_args.lower().startswith('callbackpot'):
                if not 'label' in init_args:
                    init_args = init_args + ' label=%d' % id(self)
            else:
                # if param_str missing, try to find default set of QUIP params,
                # falling back on a do-nothing parameter string.
                if param_str is None and pot1 is None and pot2 is None:
                    try:
                        param_str = quip_xml_parameters(init_args)
                    except IOError:
                        param_str = r'<params></params>'

        if kwargs != {}:
            if init_args is not None:
                init_args = init_args + ' ' + dict_to_args_str(kwargs)
            else:
                init_args = dict_to_args_str(kwargs)

        # Change to the xml directory to initialise, so that extra files
        # like sparseX can be found.
        old_dir = os.getcwd()
        try:
            if param_dirname is not None:
                os.chdir(param_dirname)

            _potential.Potential.__init__(self,
                                          init_args,
                                          pot1=pot1,
                                          pot2=pot2,
                                          param_str=param_str,
                                          bulk_scale=bulk_scale,
                                          mpi_obj=mpi_obj,
                                          fpointer=fpointer,
                                          finalise=finalise,
                                          error=error)
        finally:
            os.chdir(old_dir)

        if init_args is not None and init_args.lower().startswith(
                'callbackpot'):
            _potential.Potential.set_callback(self, Potential.callback)

            if callback is not None:
                self.set_callback(callback)

            if calculator is not None:
                self.set_callback(calculator_callback_factory(calculator))

        if atoms is not None:
            atoms.set_calculator(self)

        self.name = init_args
Exemple #7
0
    def __init__(self,
                 init_args=None,
                 pot1=None,
                 pot2=None,
                 param_str=None,
                 param_filename=None,
                 bulk_scale=None,
                 mpi_obj=None,
                 callback=None,
                 calculator=None,
                 atoms=None,
                 calculation_always_required=False,
                 fpointer=None,
                 finalise=True,
                 error=None,
                 **kwargs):

        self._calc_args = {}
        self._default_properties = []
        self.calculation_always_required = calculation_always_required
        Calculator.__init__(self, atoms=atoms)

        if callback is not None or calculator is not None:
            if init_args is None:
                init_args = 'callbackpot'

        if param_filename is not None:
            param_str = open(param_filename).read()

        if init_args is None and param_str is None:
            raise ValueError('Need one of init_args,param_str,param_filename')

        if init_args is not None:
            if init_args.lower().startswith('callbackpot'):
                if not 'label' in init_args:
                    init_args = init_args + ' label=%d' % id(self)
            else:
                # if param_str missing, try to find default set of QUIP params
                if param_str is None and pot1 is None and pot2 is None:
                    param_str = quip_xml_parameters(init_args)

        if kwargs != {}:
            if init_args is not None:
                init_args = init_args + ' ' + dict_to_args_str(kwargs)
            else:
                init_args = dict_to_args_str(kwargs)

        _potential.Potential.__init__(self,
                                      init_args,
                                      pot1=pot1,
                                      pot2=pot2,
                                      param_str=param_str,
                                      bulk_scale=bulk_scale,
                                      mpi_obj=mpi_obj,
                                      fpointer=fpointer,
                                      finalise=finalise,
                                      error=error)

        if init_args is not None and init_args.lower().startswith(
                'callbackpot'):
            _potential.Potential.set_callback(self, Potential.callback)

            if callback is not None:
                self.set_callback(callback)

            if calculator is not None:
                self.set_callback(calculator_callback_factory(calculator))

        if atoms is not None:
            atoms.set_calculator(self)

        self.name = init_args
Exemple #8
0
    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
Exemple #9
0
    def __init__(self,
                 init_args=None,
                 pot1=None,
                 pot2=None,
                 param_str=None,
                 param_filename=None,
                 bulk_scale=None,
                 mpi_obj=None,
                 callback=None,
                 calculator=None,
                 cutoff_skin=1.0,
                 atoms=None,
                 fpointer=None,
                 finalise=True,
                 error=None,
                 **kwargs):

        self.atoms = None
        self._prev_atoms = None
        self.energy = None
        self.energies = None
        self.forces = None
        self.stress = None
        self.stresses = None
        self.elastic_constants = None
        self.unrelaxed_elastic_constants = None
        self.numeric_forces = None
        self._calc_args = {}
        self._default_quantities = []
        self.cutoff_skin = cutoff_skin

        if callback is not None or calculator is not None:
            if init_args is None:
                init_args = 'callbackpot'

        if param_filename is not None:
            param_str = open(param_filename).read()

        if init_args is None and param_str is None:
            raise ValueError('Need one of init_args,param_str,param_filename')

        if init_args is not None:
            if init_args.lower().startswith('callbackpot'):
                if not 'label' in init_args:
                    init_args = init_args + ' label=%d' % id(self)
            else:
                # if param_str missing, try to find default set of QUIP params
                if param_str is None and pot1 is None and pot2 is None:
                    param_str = quip_xml_parameters(init_args)

        if kwargs != {}:
            if init_args is not None:
                init_args = init_args + ' ' + dict_to_args_str(kwargs)
            else:
                init_args = dict_to_args_str(kwargs)

        _potential.Potential.__init__(self,
                                      init_args,
                                      pot1=pot1,
                                      pot2=pot2,
                                      param_str=param_str,
                                      bulk_scale=bulk_scale,
                                      mpi_obj=mpi_obj,
                                      fpointer=fpointer,
                                      finalise=finalise,
                                      error=error)

        if init_args is not None and init_args.lower().startswith(
                'callbackpot'):
            _potential.Potential.set_callback(self, Potential.callback)

            if callback is not None:
                self.set_callback(callback)

            if calculator is not None:
                self.set_callback(calculator_callback_factory(calculator))

        if atoms is not None:
            atoms.set_calculator(self)