def test_Alchemical_tools_energy_variation(): PT_CHARGE_INT = point_charge_integral(basis, R, q, transform=molecule.mo.coeffs.T, coord_type="cartesian") dE_1 = 0.0 for i_c, i in enumerate(molecule.mo.occs): if i != 0: dE_1 = dE_1 + PT_CHARGE_INT[i_c][i_c][0] dE_2_hf = 0.0 dE_2_lda = 0.0 for s in range(2): for i_c, i in enumerate(occupied_ind[s]): for a_c, a in enumerate(virtual_ind[s]): ias = (s * len(occupied_ind[0]) * len(virtual_ind[0]) + i_c * len(virtual_ind[s]) + a_c) for t in range(2): for j_c, j in enumerate(occupied_ind[t]): for b_c, b in enumerate(virtual_ind[t]): jbt = (t * len(occupied_ind[0]) * len(virtual_ind[0]) + j_c * len(virtual_ind[t]) + b_c) dv_ias = PT_CHARGE_INT[a][i][0] dv_jbt = PT_CHARGE_INT[b][j][0] dE_2_hf = dE_2_hf - dv_ias * dv_jbt * M_HF_inv[ ias][jbt] dE_2_lda = dE_2_lda - dv_ias * dv_jbt * M_LDA_inv[ ias][jbt] assert np.allclose(alc_hf.energy_variation(), np.array([dE_1, dE_2_hf])) assert np.allclose(alc_lda.energy_variation(), np.array([dE_1, dE_2_lda]))
def energy_variation(self): """Return the variation of the MO energies Return ------ dE: 2D ndarray dE[0] is the first order energy variation dE[1] is the second order energy variation""" M_inv = self.M_inverse dv = np.sum( point_charge_integral( self._basis, self._pos, self._val, transform=self._molecule.mo.coeffs.T, coord_type=self._coord_type, ), axis=-1, ) indices = self.K_indices() dE = np.sum(np.diag(dv) * self._molecule.mo.occs) dv = np.array([ dv[indices[0][0]][:, indices[1][0]], dv[indices[0][1]][:, indices[1][1]] ]).reshape([2 * self.M_block_size]) dE = np.array( [dE, -np.dot(dv[:, None].T, M_inv).dot(dv[:, None])[0, 0]]) return dE
def test_point_charge_mix(): """Test gbasis.integrals.point_charge.point_charge_mix.""" basis_dict = parse_nwchem(find_datafile("data_sto6g.nwchem")) basis = make_contractions(basis_dict, ["Kr"], np.array([[0, 0, 0]])) point_charge_obj = PointChargeIntegral(basis) points_coords = np.random.rand(5, 3) points_charge = np.random.rand(5) assert np.allclose( point_charge_obj.construct_array_mix( ["spherical"] * 8, points_coords=points_coords, points_charge=points_charge ), point_charge_integral(basis, points_coords, points_charge, coord_type=["spherical"] * 8), )
def test_Alchemical_tools_density_matrix_variation(): PT_CHARGE_INT = point_charge_integral(basis, R, q, transform=molecule.mo.coeffs.T, coord_type="cartesian") dP_hf = np.array([np.diag(molecule.mo.occsa), np.diag(molecule.mo.occsb)]) dP_hf = 0 * dP_hf dP_lda = 0 * dP_hf for s in range(2): for i_c, i in enumerate(occupied_ind[s]): for a_c, a in enumerate(virtual_ind[s]): ias = (s * len(occupied_ind[0]) * len(virtual_ind[0]) + i_c * len(virtual_ind[s]) + a_c) for t in range(2): for j_c, j in enumerate(occupied_ind[t]): for b_c, b in enumerate(virtual_ind[t]): jbt = (t * len(occupied_ind[0]) * len(virtual_ind[0]) + j_c * len(virtual_ind[t]) + b_c) dv_ias = PT_CHARGE_INT[a][i][0] if t == 0: dP_hf[t][j][b] = (dP_hf[t][j][b] - M_HF_inv[ias][jbt] * dv_ias) dP_hf[t][b][j] = (dP_hf[t][b][j] - M_HF_inv[ias][jbt] * dv_ias) dP_lda[t][j][b] = ( dP_lda[t][j][b] - M_LDA_inv[ias][jbt] * dv_ias) dP_lda[t][b][j] = ( dP_lda[t][b][j] - M_LDA_inv[ias][jbt] * dv_ias) if t == 1: dP_hf[t][j - 19][b - 19] = ( dP_hf[t][j - 19][b - 19] - M_HF_inv[ias][jbt] * dv_ias) dP_hf[t][b - 19][j - 19] = ( dP_hf[t][b - 19][j - 19] - M_HF_inv[ias][jbt] * dv_ias) dP_lda[t][j - 19][b - 19] = ( dP_lda[t][j - 19][b - 19] - M_LDA_inv[ias][jbt] * dv_ias) dP_lda[t][b - 19][j - 19] = ( dP_lda[t][b - 19][j - 19] - M_LDA_inv[ias][jbt] * dv_ias) assert np.allclose(alc_hf.density_matrix_variation(), dP_hf) assert np.allclose(alc_lda.density_matrix_variation(), dP_lda)
def density_matrix_variation(self): """Return density matrix variation in MO basis Return ------ dP: 3D ndarray dP[0] is the alpha density matrix variation dP[1] is the beta density matrix variation""" M_inv = self.M_inverse dv = np.sum( point_charge_integral( self._basis, self._pos, self._val, transform=self._molecule.mo.coeffs.T, coord_type=self._coord_type, ), axis=-1, ) indices = self.K_indices() dv = np.array([ dv[indices[0][0]][:, indices[1][0]].reshape([self.M_block_size]), dv[indices[0][1]][:, indices[1][1]].reshape([self.M_block_size]), ]).reshape([2 * self.M_block_size]) dP = -np.dot(M_inv, dv) dP = np.array([ dP[:self.M_block_size].reshape( [len(indices[0][0]), len(indices[1][0])]), dP[self.M_block_size:].reshape( [len(indices[0][0]), len(indices[1][0])]), ]) dP = np.array([ np.block([ [np.zeros([len(indices[0][0]), len(indices[0][0])]), dP[0]], [dP[0].T, np.zeros([len(indices[1][0]), len(indices[1][0])])], ]), np.block([ [np.zeros([len(indices[0][1]), len(indices[0][1])]), dP[1]], [dP[1].T, np.zeros([len(indices[1][1]), len(indices[1][1])])], ]), ]) return dP
def test_point_charge_lincomb(): """Test gbasis.integrals.point_charge.point_charge_lincomb.""" basis_dict = parse_nwchem(find_datafile("data_sto6g.nwchem")) basis = make_contractions(basis_dict, ["Kr"], np.array([[0, 0, 0]])) point_charge_obj = PointChargeIntegral(basis) points_coords = np.random.rand(5, 3) points_charge = np.random.rand(5) transform = np.random.rand(14, 18) assert np.allclose( point_charge_obj.construct_array_lincomb( transform, "spherical", points_coords=points_coords, points_charge=points_charge ), point_charge_integral( basis, points_coords=points_coords, points_charge=points_charge, transform=transform ), )
def nuclear_electron_attraction_integral(basis, nuclear_coords, nuclear_charges, transform=None, coord_type="spherical"): """Return the nuclear electron attraction integrals of the basis set in the Cartesian form. Parameters ---------- basis : list/tuple of GeneralizedContractionShell Contracted Cartesian Gaussians (of the same shell) that will be used to construct an array. nuclear_coords : np.ndarray(N_nuc, 3) Coordinates of each atom. nuclear_charges : np.ndarray(N_nuc) Charges of each atom. transform : np.ndarray(K, K_cont) Transformation matrix from the basis set in the given coordinate system (e.g. AO) to linear combinations of contractions (e.g. MO). Transformation is applied to the left, i.e. the sum is over the index 1 of `transform` and index 0 of the array for contractions. Default is no transformation. coord_type : {"cartesian", list/tuple of "cartesian" or "spherical", "spherical"} Types of the coordinate system for the contractions. If "cartesian", then all of the contractions are treated as Cartesian contractions. If "spherical", then all of the contractions are treated as spherical contractions. If list/tuple, then each entry must be a "cartesian" or "spherical" to specify the coordinate type of each `GeneralizedContractionShell` instance. Default value is "spherical". Returns ------- array : np.ndarray(K_cart, K_cart) Array associated with the given set of contracted Cartesian Gaussians. Dimensions 0 and 1 are associated with the contracted Cartesian Gaussians. `K_cart` is the total number of Cartesian contractions within the instance. """ return np.sum( point_charge_integral(basis, nuclear_coords, nuclear_charges, transform=transform, coord_type=coord_type), axis=2, )
def test_point_charge_cartesian(): """Test gbasis.integrals.point_charge.point_charge_cartesian.""" basis_dict = parse_nwchem(find_datafile("data_sto6g.nwchem")) basis = make_contractions(basis_dict, ["Kr"], np.array([[0, 0, 0]])) point_charge_obj = PointChargeIntegral(basis) points_coords = np.random.rand(5, 3) points_charges = np.random.rand(5) assert np.allclose( point_charge_obj.construct_array_cartesian( points_coords=points_coords, points_charges=points_charges), point_charge_integral( basis, points_coords=points_coords, points_charges=points_charges, coord_type="cartesian", ), )
def test_nuclear_electron_attraction_spherical(): """Test gbasis.integrals.nuclear_electron_attraction.nuclear_electron_attraction_spherical.""" basis_dict = parse_nwchem(find_datafile("data_sto6g.nwchem")) basis = make_contractions(basis_dict, ["Kr"], np.array([[0, 0, 0]])) nuclear_coords = np.random.rand(5, 3) nuclear_charges = np.random.rand(5) ref = point_charge_integral(basis, nuclear_coords, nuclear_charges, coord_type="spherical") assert np.allclose( ref[:, :, 0] + ref[:, :, 1] + ref[:, :, 2] + ref[:, :, 3] + ref[:, :, 4], nuclear_electron_attraction_integral(basis, nuclear_coords, nuclear_charges, coord_type="spherical"), )
def test_nuclear_electron_attraction_lincomb(): """Test gbasis.integrals.nuclear_electron_attraction.nuclear_electron_attraction_lincomb.""" basis_dict = parse_nwchem(find_datafile("data_sto6g.nwchem")) basis = make_contractions(basis_dict, ["Kr"], np.array([[0, 0, 0]])) nuclear_coords = np.random.rand(5, 3) nuclear_charges = np.random.rand(5) transform = np.random.rand(14, 18) ref = point_charge_integral(basis, nuclear_coords, nuclear_charges, transform=transform) assert np.allclose( ref[:, :, 0] + ref[:, :, 1] + ref[:, :, 2] + ref[:, :, 3] + ref[:, :, 4], nuclear_electron_attraction_integral(basis, nuclear_coords, nuclear_charges, transform=transform), )
def nuclear_electron_attraction_integral(basis, nuclear_coords, nuclear_charges, transform=None, coord_type="spherical"): """Return the nuclear electron attraction integrals of the basis set in the Cartesian form. Parameters ---------- basis : list/tuple of GeneralizedContractionShell Contracted Cartesian Gaussians (of the same shell) that will be used to construct an array. nuclear_coords : np.ndarray(N_nuc, 3) Coordinates of each atom. nuclear_charges : np.ndarray(N_nuc) Charges of each atom. coord_type : {"cartesian", list/tuple of "cartesian" or "spherical", "spherical"} Types of the coordinate system for the contractions. If "cartesian", then all of the contractions are treated as Cartesian contractions. If "spherical", then all of the contractions are treated as spherical contractions. If list/tuple, then each entry must be a "cartesian" or "spherical" to specify the coordinate type of each GeneralizedContractionShell instance. Default value is "spherical". Returns ------- array : np.ndarray(K_cart, K_cart) Array associated with the given set of contracted Cartesian Gaussians. First and second indices of the array are associated with the contracted Cartesian Gaussians. `K_cart` is the total number of Cartesian contractions within the instance. """ return np.sum( point_charge_integral(basis, nuclear_coords, nuclear_charges, transform=transform, coord_type=coord_type), axis=2, )
def electrostatic_potential( basis, one_density_matrix, points, nuclear_coords, nuclear_charges, transform=None, coord_type="spherical", threshold_dist=0.0, ): r"""Return the electrostatic potentials of the basis set in the Cartesian form. Parameters ---------- basis : list/tuple of GeneralizedContractionShell Shells of generalized contractions. one_density_matrix : np.ndarray(K_orbs, K_orbs) One-electron density matrix in terms of the given basis set. If the basis is transformed using `transform` keyword, then the density matrix is assumed to be expressed with respect to the transformed basis set. points : np.ndarray(N, 3) Cartesian coordinates of the points in space (in atomic units) where the basis functions are evaluated. Rows correspond to the points and columns correspond to the :math:`x, y, \text{and} z` components. nuclear_coords : np.ndarray(N_nuc, 3) Cartesian coordinates of each atom. Rows correspond to the atoms and columns correspond to the :math:`x, y, \text{and} z` components. nuclear_charges : np.ndarray(N_nuc) Charges of each atom. transform : np.ndarray(K_orbs, K_cont) Transformation matrix from the basis set in the given coordinate system (e.g. AO) to linear combinations of contractions (e.g. MO). Transformation is applied to the left, i.e. the sum is over the index 1 of `transform` and index 0 of the array for contractions. Default is no transformation. coord_type : {"cartesian", "spherical", list/tuple of "cartesian" or "spherical"} Types of the coordinate system for the contractions. If "cartesian", then all of the contractions are treated as Cartesian contractions. If "spherical", then all of the contractions are treated as spherical contractions. If list/tuple, then each entry must be a "cartesian" or "spherical" to specify the coordinate type of each `GeneralizedContractionShell` instance. threshold_dist : {float, 0.0} Threshold for rejecting nuclei whose distances to the points are less than the provided value. i.e. nuclei that are closer to the point than the threshold are discarded when computing the electrostatic potential of the point. Default value is 0.0, i.e. no nuclei are discarded. Returns ------- array : np.ndarray(N) Electrostatic potential evaluated at the given points. Raises ------ TypeError If `one_density_matrix` is not a two-dimensional `numpy` array. If `nuclear_coords` is not a two-dimensional `numpy` array with 3 columns. If `nuclear_charges` is not a one-dimensional `numpy` array. If `threshold_dist` is not an integer or float. ValueError If `one_density_matrix` is not a symmetric (square) matrix. If number of rows in `nuclear_coords` is not equal to the number of elements in `nuclear_charges`. If `threshold_dist` is less than 0. """ # pylint: disable=R0912 if not (isinstance(one_density_matrix, np.ndarray) and one_density_matrix.ndim == 2): raise TypeError( "`one_density_matrix_cart` must be given as a two-dimensional `numpy` array." ) if not (isinstance(nuclear_coords, np.ndarray) and nuclear_coords.ndim == 2 and nuclear_coords.shape[1] == 3): raise TypeError( "`nuclear_coords` must be a two-dimensional `numpy` array with 3 columns." ) if not (isinstance(nuclear_charges, np.ndarray) and nuclear_charges.ndim == 1): raise TypeError( "`nuclear_charges` must be a one-dimensional `numpy` array.") if not (one_density_matrix.shape[0] == one_density_matrix.shape[1] and np.allclose(one_density_matrix, one_density_matrix.T)): raise ValueError( "`one_density_matrix_cart` must be a symmetric (square) matrix.") if nuclear_coords.shape[0] != nuclear_charges.size: raise ValueError( "Number of rows in `nuclear_coords` must be equal to the number of elements in " "`nuclear_charges`.") if not isinstance(threshold_dist, (int, float)): raise TypeError("`threshold_dist` must be an integer or float.") if threshold_dist < 0: raise ValueError( "`threshold_dist` must be greater than or equal to zero.") if coord_type == "cartesian": if sum(cont.num_cart * cont.num_seg_cont for cont in basis) != one_density_matrix.shape[0]: raise ValueError( "`one_density_matrix` does not have number of rows/columns that is equal to the " "total number of Cartesian contractions (atomic orbitals).") elif coord_type == "spherical": if sum(cont.num_sph * cont.num_seg_cont for cont in basis) != one_density_matrix.shape[0]: raise ValueError( "`one_density_matrix` does not have number of rows/columns that is equal to the " "total number of spherical contractions (atomic orbitals).") elif isinstance(coord_type, (list, tuple)): if (sum(cont.num_sph * cont.num_seg_cont if j == "spherical" else cont.num_cart * cont.num_seg_cont for cont, j in zip(basis, coord_type)) != one_density_matrix.shape[0]): raise ValueError( "`one_density_matrix` does not have number of rows/columns that is equal to the " "total number of contractions in the given coordinate systems (atomic orbitals)." ) else: raise TypeError( "`coord_type` must be 'spherical', 'cartesian', or a list/tuple of these strings." ) hartree_potential = point_charge_integral(basis, points, -np.ones(points.shape[0]), transform=transform, coord_type=coord_type) hartree_potential *= one_density_matrix[:, :, None] hartree_potential = np.sum(hartree_potential, axis=(0, 1)) # silence warning for dividing by zero old_settings = np.seterr(divide="ignore") external_potential = (nuclear_charges[None, :] / np.sum( (points[:, :, None] - nuclear_coords.T[None, :, :])**2, axis=1)**0.5) # zero out potentials of elements that are too close to the nucleus external_potential[external_potential > 1.0 / np.array(threshold_dist)] = 0 # restore old settings np.seterr(**old_settings) # sum over potentials for each dimension external_potential = -np.sum(external_potential, axis=1) return -(external_potential + hartree_potential)