def _rotate_orbitals_in_qmolecule( qmolecule: QMolecule, orbital_rotation: 'OrbitalRotation') -> None: """ Rotates the orbitals by applying a modified a anti-hermitian matrix (orbital_rotation.matrix_a) onto the MO coefficients matrix and recomputes all the quantities dependent on the MO coefficients. Be aware that qmolecule is modified when this executes. Args: qmolecule: instance of QMolecule class orbital_rotation: instance of OrbitalRotation class """ # 1 and 2 electron integrals (required) from AO to MO basis qmolecule.mo_coeff = np.matmul(qmolecule.mo_coeff, orbital_rotation.matrix_a) qmolecule.mo_onee_ints = qmolecule.oneeints2mo(qmolecule.hcore, qmolecule.mo_coeff) # support for unrestricted spins if qmolecule.mo_coeff_b is not None: qmolecule.mo_coeff_b = np.matmul(qmolecule.mo_coeff_b, orbital_rotation.matrix_b) qmolecule.mo_onee_ints_b = qmolecule.oneeints2mo( qmolecule.hcore, qmolecule.mo_coeff) qmolecule.mo_eri_ints = qmolecule.twoeints2mo(qmolecule.eri, qmolecule.mo_coeff) if qmolecule.mo_coeff_b is not None: mo_eri_b = qmolecule.twoeints2mo(qmolecule.eri, qmolecule.mo_coeff_b) norbs = qmolecule.mo_coeff.shape[0] qmolecule.mo_eri_ints_bb = mo_eri_b.reshape( norbs, norbs, norbs, norbs) qmolecule.mo_eri_ints_ba = qmolecule.twoeints2mo_general( qmolecule.eri, qmolecule.mo_coeff_b, qmolecule.mo_coeff_b, qmolecule.mo_coeff, qmolecule.mo_coeff) qmolecule.mo_eri_ints_ba = qmolecule.mo_eri_ints_ba.reshape( norbs, norbs, norbs, norbs) # dipole integrals (if available) from AO to MO if qmolecule.x_dip_ints is not None: qmolecule.x_dip_mo_ints = qmolecule.oneeints2mo( qmolecule.x_dip_ints, qmolecule.mo_coeff) qmolecule.y_dip_mo_ints = qmolecule.oneeints2mo( qmolecule.y_dip_ints, qmolecule.mo_coeff) qmolecule.z_dip_mo_ints = qmolecule.oneeints2mo( qmolecule.z_dip_ints, qmolecule.mo_coeff) # support for unrestricted spins if qmolecule.mo_coeff_b is not None and qmolecule.x_dip_ints is not None: qmolecule.x_dip_mo_ints_b = qmolecule.oneeints2mo( qmolecule.x_dip_ints, qmolecule.mo_coeff_b) qmolecule.y_dip_mo_ints_b = qmolecule.oneeints2mo( qmolecule.y_dip_ints, qmolecule.mo_coeff_b) qmolecule.z_dip_mo_ints_b = qmolecule.oneeints2mo( qmolecule.z_dip_ints, qmolecule.mo_coeff_b)
def _parse_matrix_file(self, fname, useao2e=False): # get_driver_class is used here because the discovery routine will load all the gaussian # binary dependencies, if not loaded already. It won't work without it. try: # add gauopen to sys.path so that binaries can be loaded gauopen_directory = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'gauopen') if gauopen_directory not in sys.path: sys.path.insert(0, gauopen_directory) # pylint: disable=import-outside-toplevel from .gauopen.QCMatEl import MatEl except ImportError as mnfe: msg = ('qcmatrixio extension not found. ' 'See Gaussian driver readme to build qcmatrixio.F using f2py') \ if mnfe.name == 'qcmatrixio' else str(mnfe) logger.info(msg) raise QiskitChemistryError(msg) from mnfe mel = MatEl(file=fname) logger.debug('MatrixElement file:\n%s', mel) # Create driver level molecule object and populate _q_ = QMolecule() _q_.origin_driver_version = mel.gversion # Energies and orbits _q_.hf_energy = mel.scalar('ETOTAL') _q_.nuclear_repulsion_energy = mel.scalar('ENUCREP') _q_.num_orbitals = 0 # updated below from orbital coeffs size _q_.num_alpha = (mel.ne + mel.multip - 1) // 2 _q_.num_beta = (mel.ne - mel.multip + 1) // 2 moc = self._get_matrix(mel, 'ALPHA MO COEFFICIENTS') moc_b = self._get_matrix(mel, 'BETA MO COEFFICIENTS') if np.array_equal(moc, moc_b): logger.debug('ALPHA and BETA MO COEFFS identical, keeping only ALPHA') moc_b = None _q_.num_orbitals = moc.shape[0] _q_.mo_coeff = moc _q_.mo_coeff_b = moc_b orbs_energy = self._get_matrix(mel, 'ALPHA ORBITAL ENERGIES') _q_.orbital_energies = orbs_energy orbs_energy_b = self._get_matrix(mel, 'BETA ORBITAL ENERGIES') _q_.orbital_energies_b = orbs_energy_b if moc_b is not None else None # Molecule geometry _q_.molecular_charge = mel.icharg _q_.multiplicity = mel.multip _q_.num_atoms = mel.natoms _q_.atom_symbol = [] _q_.atom_xyz = np.empty([mel.natoms, 3]) syms = mel.ian xyz = np.reshape(mel.c, (_q_.num_atoms, 3)) for n_i in range(0, _q_.num_atoms): _q_.atom_symbol.append(QMolecule.symbols[syms[n_i]]) for idx in range(xyz.shape[1]): coord = xyz[n_i][idx] if abs(coord) < 1e-10: coord = 0 _q_.atom_xyz[n_i][idx] = coord # 1 and 2 electron integrals hcore = self._get_matrix(mel, 'CORE HAMILTONIAN ALPHA') logger.debug('CORE HAMILTONIAN ALPHA %s', hcore.shape) hcore_b = self._get_matrix(mel, 'CORE HAMILTONIAN BETA') if np.array_equal(hcore, hcore_b): # From Gaussian interfacing documentation: "The two # core Hamiltonians are identical unless # a Fermi contact perturbation has been applied." logger.debug('CORE HAMILTONIAN ALPHA and BETA identical, keeping only ALPHA') hcore_b = None logger.debug('CORE HAMILTONIAN BETA %s', '- Not present' if hcore_b is None else hcore_b.shape) kinetic = self._get_matrix(mel, 'KINETIC ENERGY') logger.debug('KINETIC ENERGY %s', kinetic.shape) overlap = self._get_matrix(mel, 'OVERLAP') logger.debug('OVERLAP %s', overlap.shape) mohij = QMolecule.oneeints2mo(hcore, moc) mohij_b = None if moc_b is not None: mohij_b = QMolecule.oneeints2mo(hcore if hcore_b is None else hcore_b, moc_b) eri = self._get_matrix(mel, 'REGULAR 2E INTEGRALS') logger.debug('REGULAR 2E INTEGRALS %s', eri.shape) if moc_b is None and mel.matlist.get('BB MO 2E INTEGRALS') is not None: # It seems that when using ROHF, where alpha and beta coeffs are # the same, that integrals # for BB and BA are included in the output, as well as just AA # that would have been expected # Using these fails to give the right answer (is ok for UHF). # So in this case we revert to # using 2 electron ints in atomic basis from the output and # converting them ourselves. useao2e = True logger.info( 'Identical A and B coeffs but BB ints are present - using regular 2E ints instead') if useao2e: # eri are 2-body in AO. We can convert to MO via the QMolecule # method but using ints in MO already, as in the else here, is better mohijkl = QMolecule.twoeints2mo(eri, moc) mohijkl_bb = None mohijkl_ba = None if moc_b is not None: mohijkl_bb = QMolecule.twoeints2mo(eri, moc_b) mohijkl_ba = QMolecule.twoeints2mo_general(eri, moc_b, moc_b, moc, moc) else: # These are in MO basis but by default will be reduced in size by # frozen core default so to use them we need to add Window=Full # above when we augment the config mohijkl = self._get_matrix(mel, 'AA MO 2E INTEGRALS') logger.debug('AA MO 2E INTEGRALS %s', mohijkl.shape) mohijkl_bb = self._get_matrix(mel, 'BB MO 2E INTEGRALS') logger.debug('BB MO 2E INTEGRALS %s', '- Not present' if mohijkl_bb is None else mohijkl_bb.shape) mohijkl_ba = self._get_matrix(mel, 'BA MO 2E INTEGRALS') logger.debug('BA MO 2E INTEGRALS %s', '- Not present' if mohijkl_ba is None else mohijkl_ba.shape) _q_.hcore = hcore _q_.hcore_b = hcore_b _q_.kinetic = kinetic _q_.overlap = overlap _q_.eri = eri _q_.mo_onee_ints = mohij _q_.mo_onee_ints_b = mohij_b _q_.mo_eri_ints = mohijkl _q_.mo_eri_ints_bb = mohijkl_bb _q_.mo_eri_ints_ba = mohijkl_ba # dipole moment dipints = self._get_matrix(mel, 'DIPOLE INTEGRALS') dipints = np.einsum('ijk->kji', dipints) _q_.x_dip_ints = dipints[0] _q_.y_dip_ints = dipints[1] _q_.z_dip_ints = dipints[2] _q_.x_dip_mo_ints = QMolecule.oneeints2mo(dipints[0], moc) _q_.x_dip_mo_ints_b = None _q_.y_dip_mo_ints = QMolecule.oneeints2mo(dipints[1], moc) _q_.y_dip_mo_ints_b = None _q_.z_dip_mo_ints = QMolecule.oneeints2mo(dipints[2], moc) _q_.z_dip_mo_ints_b = None if moc_b is not None: _q_.x_dip_mo_ints_b = QMolecule.oneeints2mo(dipints[0], moc_b) _q_.y_dip_mo_ints_b = QMolecule.oneeints2mo(dipints[1], moc_b) _q_.z_dip_mo_ints_b = QMolecule.oneeints2mo(dipints[2], moc_b) nucl_dip = np.einsum('i,ix->x', syms, xyz) nucl_dip = np.round(nucl_dip, decimals=8) _q_.nuclear_dipole_moment = nucl_dip _q_.reverse_dipole_sign = True return _q_