def do_moments(self): if log.do_medium: log('Computing cartesian and pure AIM multipoles and radial AIM moments.' ) ncart = get_ncart_cumul(self.lmax) cartesian_multipoles, new1 = self._cache.load( 'cartesian_multipoles', alloc=(self._system.natom, ncart), tags='o') npure = get_npure_cumul(self.lmax) pure_multipoles, new1 = self._cache.load('pure_multipoles', alloc=(self._system.natom, npure), tags='o') nrad = self.lmax + 1 radial_moments, new2 = self._cache.load('radial_moments', alloc=(self._system.natom, nrad), tags='o') if new1 or new2: self.do_partitioning() for i in xrange(self._system.natom): # 1) Define a 'window' of the integration grid for this atom center = self._system.coordinates[i] grid = self.get_grid(i) # 2) Compute the AIM aim = self.get_moldens(i) * self.cache.load('at_weights', i) # 3) Compute weight corrections (TODO: needs to be assessed!) wcor = self.get_wcor(i) # 4) Compute Cartesian multipole moments # The minus sign is present to account for the negative electron # charge. cartesian_multipoles[i] = -grid.integrate( aim, wcor, center=center, lmax=self.lmax, mtype=1) cartesian_multipoles[i, 0] += self.system.pseudo_numbers[i] # 5) Compute Pure multipole moments # The minus sign is present to account for the negative electron # charge. pure_multipoles[i] = -grid.integrate( aim, wcor, center=center, lmax=self.lmax, mtype=2) pure_multipoles[i, 0] += self.system.pseudo_numbers[i] # 6) Compute Radial moments # For the radial moments, it is not common to put a minus sign # for the negative electron charge. radial_moments[i] = grid.integrate(aim, wcor, center=center, lmax=self.lmax, mtype=3)
def do_moments(self): ncart = get_ncart_cumul(self.lmax) cartesian_multipoles, new1 = self._cache.load('cartesian_multipoles', alloc=(self.natom, ncart), tags='o') npure = get_npure_cumul(self.lmax) pure_multipoles, new1 = self._cache.load('pure_multipoles', alloc=(self.natom, npure), tags='o') nrad = self.lmax+1 radial_moments, new2 = self._cache.load('radial_moments', alloc=(self.natom, nrad), tags='o') if new1 or new2: self.do_partitioning() if log.do_medium: log('Computing cartesian and pure AIM multipoles and radial AIM moments.') for i in xrange(self.natom): # 1) Define a 'window' of the integration grid for this atom center = self.coordinates[i] grid = self.get_grid(i) # 2) Compute the AIM aim = self.get_moldens(i)*self.cache.load('at_weights', i) # 3) Compute weight corrections wcor = self.get_wcor(i) # 4) Compute Cartesian multipole moments # The minus sign is present to account for the negative electron # charge. cartesian_multipoles[i] = -grid.integrate(aim, wcor, center=center, lmax=self.lmax, mtype=1) cartesian_multipoles[i, 0] += self.pseudo_numbers[i] # 5) Compute Pure multipole moments # The minus sign is present to account for the negative electron # charge. pure_multipoles[i] = -grid.integrate(aim, wcor, center=center, lmax=self.lmax, mtype=2) pure_multipoles[i, 0] += self.pseudo_numbers[i] # 6) Compute Radial moments # For the radial moments, it is not common to put a minus sign # for the negative electron charge. radial_moments[i] = grid.integrate(aim, wcor, center=center, lmax=self.lmax, mtype=3)
def wpart_slow_analysis(wpart, mol): """An additional and optional analysis for horton-wpart.py This analysis is currently not included in horton/part because it would imply additional changes in the API. **Arguments:** wpart An instance of a WPart class mol An instance of IOData. This instance must at least contain an obasis, exp_alpha and exp_beta (in case of unrestricted spin) object. """ # A) Compute AIM overlap operators # -------------------------------- # These are not included in the output by default because they occupy too # much space. wpart.do_partitioning() npure = get_npure_cumul(wpart.lmax) for index in xrange(wpart.natom): if log.do_medium: log('Computing overlap matrices for atom %i.' % index) # Prepare solid harmonics on grids. grid = wpart.get_grid(index) if wpart.lmax > 0: work = np.zeros((grid.size, npure - 1), float) work[:, 0] = grid.points[:, 2] - wpart.coordinates[index, 2] work[:, 1] = grid.points[:, 0] - wpart.coordinates[index, 0] work[:, 2] = grid.points[:, 1] - wpart.coordinates[index, 1] if wpart.lmax > 1: fill_pure_polynomials(work, wpart.lmax) at_weights = wpart.cache.load('at_weights', index) # Convert the weight functions to AIM overlap operators. counter = 0 overlap_operators = {} for l in xrange(wpart.lmax + 1): for m in xrange(-l, l + 1): if counter > 0: tmp = at_weights * work[:, counter - 1] else: tmp = at_weights op = mol.obasis.compute_grid_density_fock( grid.points, grid.weights, tmp) overlap_operators['olp_%05i' % counter] = op counter += 1 wpart.cache.dump(('overlap_operators', index), overlap_operators) # Correct the s-type overlap operators such that the sum is exactly # equal to the total overlap. error_overlap = mol.obasis.compute_overlap() for index in xrange(wpart.natom): atom_overlap = wpart.cache.load('overlap_operators', index)['olp_00000'] error_overlap -= atom_overlap error_overlap /= wpart.natom for index in xrange(wpart.natom): atom_overlap = wpart.cache.load('overlap_operators', index)['olp_00000'] atom_overlap += error_overlap # A') Construct the list of operators with a logical ordering # * outer loop: s, pz, px, py, ... # * inner loop: atoms operators = [] for ipure in xrange(npure): for iatom in xrange(wpart.natom): operators.append( wpart.cache.load('overlap_operators', iatom)['olp_%05i' % ipure]) # B) Compute Wiberg bond orders from the first-order density matrix # ----------------------------------------------------------------- # This is inherently limited because the second-order density matrix is # required to compute a proper density matrix. For a pure single-reference # method like HF, this is fine. In case of DFT, the interpretation of the # bond-orders is dicy. In case of Post-HF, these bond orders are plain # wrong. if log.do_medium: log('Computing bond orders.') dm_full = mol.get_dm_full() dm_spin = mol.get_dm_spin() if dm_spin is None: # closed-shell case dm_alpha = dm_full * 0.5 bond_orders, valences, free_valences = compute_bond_orders_cs( dm_alpha, operators) else: # open-shell case dm_alpha = 0.5 * (dm_full + dm_spin) dm_beta = 0.5 * (dm_full - dm_spin) dm_beta.iadd(dm_spin, -1.0) dm_beta.iscale(0.5) bond_orders, valences, free_valences = compute_bond_orders_os( dm_alpha, dm_beta, operators) wpart.cache.dump('bond_orders', bond_orders, tags='o') wpart.cache.dump('valences', valences, tags='o') wpart.cache.dump('free_valences', free_valences, tags='o') # C) Non-interacting response # --------------------------- # The current implementation is only sensible for a single-reference method # as it is based on HF/KS orbitals. The results are meaningful for both # HF and KS orbitals. if log.do_medium: log('Computing Xs response.') if dm_spin is None: if not hasattr(mol, 'exp_alpha'): return xs_response = 2 * compute_noninteracting_response( mol.exp_alpha, operators) else: if not (hasattr(mol, 'exp_alpha') and hasattr(mol, 'exp_beta')): return xs_response = compute_noninteracting_response(mol.exp_alpha, operators) xs_response += compute_noninteracting_response(mol.exp_beta, operators) wpart.cache.dump('noninteracting_response', xs_response, tags='o')
def wpart_slow_analysis(wpart, mol): """An additional and optional analysis for horton-wpart.py This analysis is currently not included in horton/part because it would imply additional changes in the API. **Arguments:** wpart An instance of a WPart class mol An instance of IOData. This instance must at least contain an obasis, exp_alpha and exp_beta (in case of unrestricted spin) object. """ # A) Compute AIM overlap operators # -------------------------------- # These are not included in the output by default because they occupy too # much space. wpart.do_partitioning() npure = get_npure_cumul(wpart.lmax) for index in xrange(wpart.natom): if log.do_medium: log('Computing overlap matrices for atom %i.' % index) # Prepare solid harmonics on grids. grid = wpart.get_grid(index) if wpart.lmax > 0: work = np.zeros((grid.size, npure - 1), float) work[:, 0] = grid.points[:, 2] - wpart.coordinates[index, 2] work[:, 1] = grid.points[:, 0] - wpart.coordinates[index, 0] work[:, 2] = grid.points[:, 1] - wpart.coordinates[index, 1] if wpart.lmax > 1: fill_pure_polynomials(work, wpart.lmax) at_weights = wpart.cache.load('at_weights', index) # Convert the weight functions to AIM overlap operators. counter = 0 overlap_operators = {} for l in xrange(wpart.lmax + 1): for m in xrange(-l, l + 1): if counter > 0: tmp = at_weights * work[:, counter - 1] else: tmp = at_weights op = mol.obasis.compute_grid_density_fock(grid.points, grid.weights, tmp) overlap_operators['olp_%05i' % counter] = op counter += 1 wpart.cache.dump(('overlap_operators', index), overlap_operators) # Correct the s-type overlap operators such that the sum is exactly # equal to the total overlap. error_overlap = mol.obasis.compute_overlap() for index in xrange(wpart.natom): atom_overlap = wpart.cache.load('overlap_operators', index)['olp_00000'] error_overlap -= atom_overlap error_overlap /= wpart.natom for index in xrange(wpart.natom): atom_overlap = wpart.cache.load('overlap_operators', index)['olp_00000'] atom_overlap += error_overlap # A') Construct the list of operators with a logical ordering # * outer loop: s, pz, px, py, ... # * inner loop: atoms operators = [] for ipure in xrange(npure): for iatom in xrange(wpart.natom): operators.append(wpart.cache.load('overlap_operators', iatom)['olp_%05i' % ipure]) # B) Compute Wiberg bond orders from the first-order density matrix # ----------------------------------------------------------------- # This is inherently limited because the second-order density matrix is # required to compute a proper density matrix. For a pure single-reference # method like HF, this is fine. In case of DFT, the interpretation of the # bond-orders is dicy. In case of Post-HF, these bond orders are plain # wrong. if log.do_medium: log('Computing bond orders.') dm_full = mol.get_dm_full() dm_spin = mol.get_dm_spin() if dm_spin is None: # closed-shell case dm_alpha = dm_full*0.5 bond_orders, valences, free_valences = compute_bond_orders_cs(dm_alpha, operators) else: # open-shell case dm_alpha = 0.5*(dm_full + dm_spin) dm_beta = 0.5*(dm_full - dm_spin) dm_beta.iadd(dm_spin, -1.0) dm_beta.iscale(0.5) bond_orders, valences, free_valences = compute_bond_orders_os(dm_alpha, dm_beta, operators) wpart.cache.dump('bond_orders', bond_orders, tags='o') wpart.cache.dump('valences', valences, tags='o') wpart.cache.dump('free_valences', free_valences, tags='o') # C) Non-interacting response # --------------------------- # The current implementation is only sensible for a single-reference method # as it is based on HF/KS orbitals. The results are meaningful for both # HF and KS orbitals. if log.do_medium: log('Computing Xs response.') if dm_spin is None: if not hasattr(mol, 'exp_alpha'): return xs_response = 2 * compute_noninteracting_response(mol.exp_alpha, operators) else: if not (hasattr(mol, 'exp_alpha') and hasattr(mol, 'exp_beta')): return xs_response = compute_noninteracting_response(mol.exp_alpha, operators) xs_response += compute_noninteracting_response(mol.exp_beta, operators) wpart.cache.dump('noninteracting_response', xs_response, tags='o')