Ejemplo n.º 1
0
    def apply_to(self, coordinates, numbers):
        """Construct a GOBasis object for the given molecular geometry

           **Arguments:**

           coordinates
                A (N, 3) float numpy array with Cartesian coordinates of the
                atoms.

           numbers
                A (N,) numpy vector with the atomic numbers.

           Note that the geometry specified by the arguments may also contain
           ghost atoms.
        """
        natom, coordinates, numbers = typecheck_geo(coordinates, numbers, need_pseudo_numbers=False)

        shell_map = []
        nprims = []
        shell_types = []
        alphas = []
        con_coeffs = []

        def get_basis(i, n):
            """Look up the basis for a given atom"""
            basis = self.index_map.get(i)
            if basis is not None:
                return basis
            basis = self.element_map.get(n)
            if basis is not None:
                return basis
            if self.default is None:
                raise KeyError('Could not find basis for atom %i.' % i)
            else:
                return self.default

        def translate_basis(basis_x, n):
            """Translate the first argument into a GOBasisAtom instance"""
            if isinstance(basis_x, basestring):
                basis_fam = go_basis_families.get(basis_x.lower())
                if basis_fam is None:
                    raise ValueError('Unknown basis family: %s' % basis_x)
                basis_atom = basis_fam.get(n)
                if basis_atom is None:
                    raise ValueError('The basis family %s does not contain element %i.' % (basis_x, n))
                return basis_atom
            elif isinstance(basis_x, GOBasisFamily):
                basis_atom = basis_x.get(n)
                if basis_atom is None:
                    raise ValueError('The basis family %s does not contain element %i.' % (basis_x.name, n))
                return basis_atom
            elif isinstance(basis_x, GOBasisAtom):
                return basis_x
            else:
                raise ValueError('Can not interpret %s as an atomic basis function.' % basis_x)

        # Loop over the atoms and fill in all the lists
        for i in xrange(natom):
            n = numbers[i]
            basis_x = get_basis(i, n)
            basis_atom = translate_basis(basis_x, n)
            basis_atom.extend(i, shell_map, nprims, shell_types, alphas, con_coeffs, self.pure)

        # Return the Gaussian basis object.
        return GOBasis(coordinates, shell_map, nprims, shell_types, alphas, con_coeffs)
Ejemplo n.º 2
0
    def __init__(self, centers, numbers, pseudo_numbers=None, agspec='medium', k=3, random_rotate=True, mode='discard'):
        '''
           **Arguments:**

           centers
                An array (N, 3) with centers for the atom-centered grids.

           numbers
                An array (N,) with atomic numbers.

           **Optional arguments:**

           pseudo_numbers
                An array (N,) with effective core charges. When not given, this
                defaults to ``numbers``.

           agspec
                A specifications of the atomic grid. This can either be an
                instance of the AtomicGridSpec object, or the first argument
                of its constructor.

           k
                The order of the switching function in Becke's weighting scheme.

           random_rotate
                Flag to control random rotation of spherical grids.

           mode
                Select one of the following options regarding atomic subgrids:

                * ``'discard'`` (the default) means that all information about
                  subgrids gets discarded.

                * ``'keep'`` means that a list of subgrids is kept, including
                  the integration weights of the local grids.

                * ``'only'`` means that only the subgrids are constructed and
                  that the computation of the molecular integration weights
                  (based on the Becke partitioning) is skipped.
        '''
        natom, centers, numbers, pseudo_numbers = typecheck_geo(centers, numbers, pseudo_numbers)
        self._centers = centers
        self._numbers = numbers
        self._pseudo_numbers = pseudo_numbers

        # check if the mode argument is valid
        if mode not in ['discard', 'keep', 'only']:
            raise ValueError('The mode argument must be \'discard\', \'keep\' or \'only\'.')

        # transform agspec into a usable format
        if not isinstance(agspec, AtomicGridSpec):
            agspec = AtomicGridSpec(agspec)
        self._agspec = agspec

        # assign attributes
        self._k = k
        self._random_rotate = random_rotate
        self._mode = mode

        # allocate memory for the grid
        size = sum(agspec.get_size(self.numbers[i], self.pseudo_numbers[i]) for i in xrange(natom))
        points = np.zeros((size, 3), float)
        weights = np.zeros(size, float)
        self._becke_weights = np.ones(size, float)

        # construct the atomic grids
        if mode != 'discard':
            atgrids = []
        else:
            atgrids = None
        offset = 0

        if mode != 'only':
            # More recent covalent radii are used than in the original work of Becke.
            # No covalent radius is defined for elements heavier than Curium and a
            # default value of 3.0 Bohr is used for heavier elements.
            cov_radii = np.array([(periodic[n].cov_radius or 3.0) for n in self.numbers])

        # The actual work:
        if log.do_medium:
            log('Preparing Becke-Lebedev molecular integration grid.')
        pb = log.progress(natom)
        for i in xrange(natom):
            atsize = agspec.get_size(self.numbers[i], self.pseudo_numbers[i])
            atgrid = AtomicGrid(
                self.numbers[i], self.pseudo_numbers[i],
                self.centers[i], agspec, random_rotate,
                points[offset:offset+atsize])
            if mode != 'only':
                atbecke_weights = self._becke_weights[offset:offset+atsize]
                becke_helper_atom(points[offset:offset+atsize], atbecke_weights, cov_radii, self.centers, i, self._k)
                weights[offset:offset+atsize] = atgrid.weights*atbecke_weights
            if mode != 'discard':
                atgrids.append(atgrid)
            offset += atsize
            pb()

        # finish
        IntGrid.__init__(self, points, weights, atgrids)

        # Some screen info
        self._log_init()
Ejemplo n.º 3
0
def setup_weights(coordinates, numbers, grid, dens=None, near=None, far=None):
    '''Define a weight function for the ESPCost

       **Arguments:**

       coordinates
            An array with shape (N, 3) containing atomic coordinates.

       numbers
            A vector with shape (N,) containing atomic numbers.

       grid
            A UniformGrid object.

       **Optional arguments:**

       dens
            The density-based criterion. This is a three-tuple with rho, lnrho0
            and sigma. rho is the atomic or the pro-atomic electron density on
            the same grid as the ESP data. lnrho0 and sigma are parameters
            defined in JCTC, 3, 1004 (2007), DOI:10.1021/ct600295n. The weight
            function takes the form::

                exp(-sigma*(ln(rho) - lnrho0)**2)

            Note that the density, rho, should not contain depletions in the
            atomic cores, as is often encountered with pseudo-potential
            computations. In that case it is recommended to construct a
            promolecular density as input for this option.

       near
            Exclude points near the nuclei. This is a dictionary with as items
            (number, (R0, gamma)).

       far
            Exclude points far away. This is a two-tuple: (R0, gamma).
    '''
    natom, coordinates, numbers = typecheck_geo(coordinates, numbers, need_pseudo_numbers=False)
    weights = np.ones(grid.shape)

    # combine three possible mask functions
    if dens is not None:
        biblio.cite('hu2007', 'for the ESP fitting weight function')
        rho, lnrho0, sigma = dens
        assert (rho.shape == grid.shape).all()
        multiply_dens_mask(rho, lnrho0, sigma, weights)
    if near is not None:
        for i in xrange(natom):
            pair = near.get(numbers[i])
            if pair is None:
                pair = near.get(0)
            if pair is None:
                continue
            r0, gamma = pair
            if r0 > 5*angstrom:
                raise ValueError('The wnear radius is excessive. Please keep it below 5 angstrom.')
            multiply_near_mask(coordinates[i], grid, r0, gamma, weights)
    if far is not None:
        r0, gamma = far
        multiply_far_mask(coordinates, grid, r0, gamma, weights)

    # double that weight goes to zero at non-periodic edges
    return weights
Ejemplo n.º 4
0
def setup_weights(coordinates, numbers, grid, dens=None, near=None, far=None):
    '''Define a weight function for the ESPCost

       **Arguments:**

       coordinates
            An array with shape (N, 3) containing atomic coordinates.

       numbers
            A vector with shape (N,) containing atomic numbers.

       grid
            A UniformGrid object.

       **Optional arguments:**

       dens
            The density-based criterion. This is a three-tuple with rho, lnrho0
            and sigma. rho is the atomic or the pro-atomic electron density on
            the same grid as the ESP data. lnrho0 and sigma are parameters
            defined in JCTC, 3, 1004 (2007), DOI:10.1021/ct600295n. The weight
            function takes the form::

                exp(-sigma*(ln(rho) - lnrho0)**2)

            Note that the density, rho, should not contain depletions in the
            atomic cores, as is often encountered with pseudo-potential
            computations. In that case it is recommended to construct a
            promolecular density as input for this option.

       near
            Exclude points near the nuclei. This is a dictionary with as items
            (number, (R0, gamma)).

       far
            Exclude points far away. This is a two-tuple: (R0, gamma).
    '''
    natom, coordinates, numbers = typecheck_geo(coordinates,
                                                numbers,
                                                need_pseudo_numbers=False)
    weights = np.ones(grid.shape)

    # combine three possible mask functions
    if dens is not None:
        biblio.cite('hu2007', 'for the ESP fitting weight function')
        rho, lnrho0, sigma = dens
        assert (rho.shape == grid.shape).all()
        multiply_dens_mask(rho, lnrho0, sigma, weights)
    if near is not None:
        for i in xrange(natom):
            pair = near.get(numbers[i])
            if pair is None:
                pair = near.get(0)
            if pair is None:
                continue
            r0, gamma = pair
            if r0 > 5 * angstrom:
                raise ValueError(
                    'The wnear radius is excessive. Please keep it below 5 angstrom.'
                )
            multiply_near_mask(coordinates[i], grid, r0, gamma, weights)
    if far is not None:
        r0, gamma = far
        multiply_far_mask(coordinates, grid, r0, gamma, weights)

    # double that weight goes to zero at non-periodic edges
    return weights
Ejemplo n.º 5
0
    def __init__(self, coordinates, numbers, pseudo_numbers, grid, moldens, spindens, local, lmax):
        '''
           **Arguments:**

           coordinates
                An array (N, 3) with centers for the atom-centered grids.

           numbers
                An array (N,) with atomic numbers.

           pseudo_numbers
                An array (N,) with effective charges. When set to None, this
                defaults to``numbers.astype(float)``.

           grid
                The integration grid

           moldens
                The spin-summed electron density on the grid.

           spindens
                The spin difference density on the grid. (Can be None)

           local
                Whether or not to use local (non-periodic) subgrids for atomic
                integrals.

           lmax
                The maximum angular momentum in multipole expansions.
        '''

        # Init base class
        JustOnceClass.__init__(self)

        # Some type checking for first three arguments
        natom, coordinates, numbers, pseudo_numbers = typecheck_geo(coordinates, numbers, pseudo_numbers)
        self._natom = natom
        self._coordinates = coordinates
        self._numbers = numbers
        self._pseudo_numbers = pseudo_numbers

        # Assign remaining arguments as attributes
        self._grid = grid
        self._moldens = moldens
        self._spindens = spindens
        self._local = local
        self._lmax = lmax

        # Caching stuff, to avoid recomputation of earlier results
        self._cache = Cache()

        # Initialize the subgrids
        if local:
            self._init_subgrids()

        # Some screen logging
        self._init_log_base()
        self._init_log_scheme()
        self._init_log_memory()
        if log.do_medium:
            log.blank()
Ejemplo n.º 6
0
    def __init__(self,
                 centers,
                 numbers,
                 pseudo_numbers=None,
                 agspec='medium',
                 k=3,
                 random_rotate=True,
                 mode='discard'):
        '''
           **Arguments:**

           centers
                An array (N, 3) with centers for the atom-centered grids.

           numbers
                An array (N,) with atomic numbers.

           **Optional arguments:**

           pseudo_numbers
                An array (N,) with effective core charges. When not given, this
                defaults to ``numbers``.

           agspec
                A specifications of the atomic grid. This can either be an
                instance of the AtomicGridSpec object, or the first argument
                of its constructor.

           k
                The order of the switching function in Becke's weighting scheme.

           random_rotate
                Flag to control random rotation of spherical grids.

           mode
                Select one of the following options regarding atomic subgrids:

                * ``'discard'`` (the default) means that all information about
                  subgrids gets discarded.

                * ``'keep'`` means that a list of subgrids is kept, including
                  the integration weights of the local grids.

                * ``'only'`` means that only the subgrids are constructed and
                  that the computation of the molecular integration weights
                  (based on the Becke partitioning) is skipped.
        '''
        natom, centers, numbers, pseudo_numbers = typecheck_geo(
            centers, numbers, pseudo_numbers)
        self._centers = centers
        self._numbers = numbers
        self._pseudo_numbers = pseudo_numbers

        # check if the mode argument is valid
        if mode not in ['discard', 'keep', 'only']:
            raise ValueError(
                'The mode argument must be \'discard\', \'keep\' or \'only\'.')

        # transform agspec into a usable format
        if not isinstance(agspec, AtomicGridSpec):
            agspec = AtomicGridSpec(agspec)
        self._agspec = agspec

        # assign attributes
        self._k = k
        self._random_rotate = random_rotate
        self._mode = mode

        # allocate memory for the grid
        size = sum(
            agspec.get_size(self.numbers[i], self.pseudo_numbers[i])
            for i in xrange(natom))
        points = np.zeros((size, 3), float)
        weights = np.zeros(size, float)
        self._becke_weights = np.ones(size, float)

        # construct the atomic grids
        if mode != 'discard':
            atgrids = []
        else:
            atgrids = None
        offset = 0

        if mode != 'only':
            # More recent covalent radii are used than in the original work of Becke.
            # No covalent radius is defined for elements heavier than Curium and a
            # default value of 3.0 Bohr is used for heavier elements.
            cov_radii = np.array([(periodic[n].cov_radius or 3.0)
                                  for n in self.numbers])

        # The actual work:
        if log.do_medium:
            log('Preparing Becke-Lebedev molecular integration grid.')
        pb = log.progress(natom)
        for i in xrange(natom):
            atsize = agspec.get_size(self.numbers[i], self.pseudo_numbers[i])
            atgrid = AtomicGrid(self.numbers[i], self.pseudo_numbers[i],
                                self.centers[i], agspec, random_rotate,
                                points[offset:offset + atsize])
            if mode != 'only':
                atbecke_weights = self._becke_weights[offset:offset + atsize]
                becke_helper_atom(points[offset:offset + atsize],
                                  atbecke_weights, cov_radii, self.centers, i,
                                  self._k)
                weights[offset:offset +
                        atsize] = atgrid.weights * atbecke_weights
            if mode != 'discard':
                atgrids.append(atgrid)
            offset += atsize
            pb()

        # finish
        IntGrid.__init__(self, points, weights, atgrids)

        # Some screen info
        self._log_init()
Ejemplo n.º 7
0
    def __init__(self, coordinates, numbers, pseudo_numbers, grid, moldens, spindens, local, lmax):
        '''
           **Arguments:**

           coordinates
                An array (N, 3) with centers for the atom-centered grids.

           numbers
                An array (N,) with atomic numbers.

           pseudo_numbers
                An array (N,) with effective charges. When set to None, this
                defaults to``numbers.astype(float)``.

           grid
                The integration grid

           moldens
                The spin-summed electron density on the grid.

           spindens
                The spin difference density on the grid. (Can be None)

           local
                Whether or not to use local (non-periodic) subgrids for atomic
                integrals.

           lmax
                The maximum angular momentum in multipole expansions.
        '''

        # Init base class
        JustOnceClass.__init__(self)

        # Some type checking for first three arguments
        natom, coordinates, numbers, pseudo_numbers = typecheck_geo(coordinates, numbers, pseudo_numbers)
        self._natom = natom
        self._coordinates = coordinates
        self._numbers = numbers
        self._pseudo_numbers = pseudo_numbers

        # Assign remaining arguments as attributes
        self._grid = grid
        self._moldens = moldens
        self._spindens = spindens
        self._local = local
        self._lmax = lmax

        # Caching stuff, to avoid recomputation of earlier results
        self._cache = Cache()

        # Initialize the subgrids
        if local:
            self._init_subgrids()

        # Some screen logging
        self._init_log_base()
        self._init_log_scheme()
        self._init_log_memory()
        if log.do_medium:
            log.blank()