def _core_wavefunction_to_file(wfn: core.Wavefunction, filename: str = None) -> Dict: """Converts a Wavefunction object to a base class Parameters ---------- wfn A Wavefunction or inherited class filename An optional filename to write the data to Returns ------- dict A dictionary and NumPy representation of the Wavefunction. """ # collect the wavefunction's variables in a dictionary indexed by varaible type # some of the data types have to be made numpy-friendly first if wfn.basisset().name().startswith("anonymous"): raise ValidationError( "Cannot serialize wavefunction with custom basissets.") wfn_data = { 'molecule': wfn.molecule().to_dict(), 'matrix': { 'Ca': wfn.Ca().to_array() if wfn.Ca() else None, 'Cb': wfn.Cb().to_array() if wfn.Cb() else None, 'Da': wfn.Da().to_array() if wfn.Da() else None, 'Db': wfn.Db().to_array() if wfn.Db() else None, 'Fa': wfn.Fa().to_array() if wfn.Fa() else None, 'Fb': wfn.Fb().to_array() if wfn.Fb() else None, 'H': wfn.H().to_array() if wfn.H() else None, 'S': wfn.S().to_array() if wfn.S() else None, 'X': wfn.lagrangian().to_array() if wfn.lagrangian() else None, 'aotoso': wfn.aotoso().to_array() if wfn.aotoso() else None, 'gradient': wfn.gradient().to_array() if wfn.gradient() else None, 'hessian': wfn.hessian().to_array() if wfn.hessian() else None }, 'vector': { 'epsilon_a': wfn.epsilon_a().to_array() if wfn.epsilon_a() else None, 'epsilon_b': wfn.epsilon_b().to_array() if wfn.epsilon_b() else None, 'frequencies': wfn.frequencies().to_array() if wfn.frequencies() else None }, 'dimension': { 'doccpi': wfn.doccpi().to_tuple(), 'frzcpi': wfn.frzcpi().to_tuple(), 'frzvpi': wfn.frzvpi().to_tuple(), 'nalphapi': wfn.nalphapi().to_tuple(), 'nbetapi': wfn.nbetapi().to_tuple(), 'nmopi': wfn.nmopi().to_tuple(), 'nsopi': wfn.nsopi().to_tuple(), 'soccpi': wfn.soccpi().to_tuple() }, 'int': { 'nalpha': wfn.nalpha(), 'nbeta': wfn.nbeta(), 'nfrzc': wfn.nfrzc(), 'nirrep': wfn.nirrep(), 'nmo': wfn.nmo(), 'nso': wfn.nso(), 'print': wfn.get_print(), }, 'string': { 'name': wfn.name(), 'module': wfn.module(), 'basisname': wfn.basisset().name() }, 'boolean': { 'PCM_enabled': wfn.PCM_enabled(), 'same_a_b_dens': wfn.same_a_b_dens(), 'same_a_b_orbs': wfn.same_a_b_orbs(), 'density_fitted': wfn.density_fitted(), 'basispuream': wfn.basisset().has_puream() }, 'float': { 'energy': wfn.energy(), 'efzc': wfn.efzc(), 'dipole_field_x': wfn.get_dipole_field_strength()[0], 'dipole_field_y': wfn.get_dipole_field_strength()[1], 'dipole_field_z': wfn.get_dipole_field_strength()[2] }, 'floatvar': wfn.scalar_variables(), 'matrixarr': {k: v.to_array() for k, v in wfn.array_variables().items()} } # yapf: disable if filename is not None: if not filename.endswith('.npy'): filename += '.npy' np.save(filename, wfn_data, allow_pickle=True) return wfn_data
def fcidump(wfn: core.Wavefunction, fname: str = 'INTDUMP', oe_ints: Optional[List] = None): """Save integrals to file in FCIDUMP format as defined in Comp. Phys. Commun. 54 75 (1989), https://doi.org/10.1016/0010-4655(89)90033-7 . Additional one-electron integrals, including orbital energies, can also be saved. This latter format can be used with the HANDE QMC code but is not standard. Parameters ---------- wfn Set of molecule, basis, orbitals from which to generate FCIDUMP file. fname Name of the integrals file, defaults to INTDUMP. oe_ints List of additional one-electron integrals to save to file. So far only EIGENVALUES is a valid option. Raises ------ ValidationError When SCF wavefunction is not RHF. Examples -------- >>> # [1] Save one- and two-electron integrals to standard FCIDUMP format >>> E, wfn = energy('scf', return_wfn=True) >>> fcidump(wfn) >>> # [2] Save orbital energies, one- and two-electron integrals. >>> E, wfn = energy('scf', return_wfn=True) >>> fcidump(wfn, oe_ints=['EIGENVALUES']) """ # Get some options reference = core.get_option('SCF', 'REFERENCE') ints_tolerance = core.get_global_option('INTS_TOLERANCE') # Some sanity checks if reference not in ['RHF', 'UHF']: raise ValidationError( 'FCIDUMP not implemented for {} references\n'.format(reference)) if oe_ints is None: oe_ints = [] molecule = wfn.molecule() docc = wfn.doccpi() frzcpi = wfn.frzcpi() frzvpi = wfn.frzvpi() active_docc = docc - frzcpi active_socc = wfn.soccpi() active_mopi = wfn.nmopi() - frzcpi - frzvpi nbf = active_mopi.sum() if wfn.same_a_b_orbs() else 2 * active_mopi.sum() nirrep = wfn.nirrep() nelectron = 2 * active_docc.sum() + active_socc.sum() irrep_map = _irrep_map(wfn) wfn_irrep = 0 for h, n_socc in enumerate(active_socc): if n_socc % 2 == 1: wfn_irrep ^= h core.print_out('Writing integrals in FCIDUMP format to ' + fname + '\n') # Generate FCIDUMP header header = '&FCI\n' header += 'NORB={:d},\n'.format(nbf) header += 'NELEC={:d},\n'.format(nelectron) header += 'MS2={:d},\n'.format(wfn.nalpha() - wfn.nbeta()) header += 'UHF=.{}.,\n'.format(not wfn.same_a_b_orbs()).upper() orbsym = '' for h in range(active_mopi.n()): for n in range(frzcpi[h], frzcpi[h] + active_mopi[h]): orbsym += '{:d},'.format(irrep_map[h]) if not wfn.same_a_b_orbs(): orbsym += '{:d},'.format(irrep_map[h]) header += 'ORBSYM={}\n'.format(orbsym) header += 'ISYM={:d},\n'.format(irrep_map[wfn_irrep]) header += '&END\n' with open(fname, 'w') as intdump: intdump.write(header) # Get an IntegralTransform object check_iwl_file_from_scf_type(core.get_global_option('SCF_TYPE'), wfn) spaces = [core.MOSpace.all()] trans_type = core.IntegralTransform.TransformationType.Restricted if not wfn.same_a_b_orbs(): trans_type = core.IntegralTransform.TransformationType.Unrestricted ints = core.IntegralTransform(wfn, spaces, trans_type) ints.transform_tei(core.MOSpace.all(), core.MOSpace.all(), core.MOSpace.all(), core.MOSpace.all()) core.print_out('Integral transformation complete!\n') DPD_info = { 'instance_id': ints.get_dpd_id(), 'alpha_MO': ints.DPD_ID('[A>=A]+'), 'beta_MO': 0 } if not wfn.same_a_b_orbs(): DPD_info['beta_MO'] = ints.DPD_ID("[a>=a]+") # Write TEI to fname in FCIDUMP format core.fcidump_tei_helper(nirrep, wfn.same_a_b_orbs(), DPD_info, ints_tolerance, fname) # Read-in OEI and write them to fname in FCIDUMP format # Indexing functions to translate from zero-based (C and Python) to # one-based (Fortran) mo_idx = lambda x: x + 1 alpha_mo_idx = lambda x: 2 * x + 1 beta_mo_idx = lambda x: 2 * (x + 1) with open(fname, 'a') as intdump: core.print_out('Writing frozen core operator in FCIDUMP format to ' + fname + '\n') if reference == 'RHF': PSIF_MO_FZC = 'MO-basis Frozen-Core Operator' moH = core.Matrix(PSIF_MO_FZC, wfn.nmopi(), wfn.nmopi()) moH.load(core.IO.shared_object(), psif.PSIF_OEI) mo_slice = core.Slice(frzcpi, frzcpi + active_mopi) MO_FZC = moH.get_block(mo_slice, mo_slice) offset = 0 for h, block in enumerate(MO_FZC.nph): il = np.tril_indices(block.shape[0]) for index, x in np.ndenumerate(block[il]): row = mo_idx(il[0][index] + offset) col = mo_idx(il[1][index] + offset) if (abs(x) > ints_tolerance): intdump.write('{:28.20E}{:4d}{:4d}{:4d}{:4d}\n'.format( x, row, col, 0, 0)) offset += block.shape[0] # Additional one-electron integrals as requested in oe_ints # Orbital energies core.print_out('Writing orbital energies in FCIDUMP format to ' + fname + '\n') if 'EIGENVALUES' in oe_ints: eigs_dump = write_eigenvalues( wfn.epsilon_a().get_block(mo_slice).to_array(), mo_idx) intdump.write(eigs_dump) else: PSIF_MO_A_FZC = 'MO-basis Alpha Frozen-Core Oper' moH_A = core.Matrix(PSIF_MO_A_FZC, wfn.nmopi(), wfn.nmopi()) moH_A.load(core.IO.shared_object(), psif.PSIF_OEI) mo_slice = core.Slice(frzcpi, active_mopi) MO_FZC_A = moH_A.get_block(mo_slice, mo_slice) offset = 0 for h, block in enumerate(MO_FZC_A.nph): il = np.tril_indices(block.shape[0]) for index, x in np.ndenumerate(block[il]): row = alpha_mo_idx(il[0][index] + offset) col = alpha_mo_idx(il[1][index] + offset) if (abs(x) > ints_tolerance): intdump.write('{:28.20E}{:4d}{:4d}{:4d}{:4d}\n'.format( x, row, col, 0, 0)) offset += block.shape[0] PSIF_MO_B_FZC = 'MO-basis Beta Frozen-Core Oper' moH_B = core.Matrix(PSIF_MO_B_FZC, wfn.nmopi(), wfn.nmopi()) moH_B.load(core.IO.shared_object(), psif.PSIF_OEI) mo_slice = core.Slice(frzcpi, active_mopi) MO_FZC_B = moH_B.get_block(mo_slice, mo_slice) offset = 0 for h, block in enumerate(MO_FZC_B.nph): il = np.tril_indices(block.shape[0]) for index, x in np.ndenumerate(block[il]): row = beta_mo_idx(il[0][index] + offset) col = beta_mo_idx(il[1][index] + offset) if (abs(x) > ints_tolerance): intdump.write('{:28.20E}{:4d}{:4d}{:4d}{:4d}\n'.format( x, row, col, 0, 0)) offset += block.shape[0] # Additional one-electron integrals as requested in oe_ints # Orbital energies core.print_out('Writing orbital energies in FCIDUMP format to ' + fname + '\n') if 'EIGENVALUES' in oe_ints: alpha_eigs_dump = write_eigenvalues( wfn.epsilon_a().get_block(mo_slice).to_array(), alpha_mo_idx) beta_eigs_dump = write_eigenvalues( wfn.epsilon_b().get_block(mo_slice).to_array(), beta_mo_idx) intdump.write(alpha_eigs_dump + beta_eigs_dump) # Dipole integrals #core.print_out('Writing dipole moment OEI in FCIDUMP format to ' + fname + '\n') # Traceless quadrupole integrals #core.print_out('Writing traceless quadrupole moment OEI in FCIDUMP format to ' + fname + '\n') # Frozen core + nuclear repulsion energy core.print_out( 'Writing frozen core + nuclear repulsion energy in FCIDUMP format to ' + fname + '\n') e_fzc = ints.get_frozen_core_energy() e_nuc = molecule.nuclear_repulsion_energy( wfn.get_dipole_field_strength()) intdump.write('{:28.20E}{:4d}{:4d}{:4d}{:4d}\n'.format( e_fzc + e_nuc, 0, 0, 0, 0)) core.print_out( 'Done generating {} with integrals in FCIDUMP format.\n'.format(fname))