def compute_the_fixed_phase_overlaps( paths_overlaps: List, path_hdf5: str, project_name: str, enumerate_from: int, nHOMO: int) -> Tuple: """ First track the unavoided crossings between Molecular orbitals and finally correct the phase for the whole trajectory. """ number_of_frames = len(paths_overlaps) # Pasth to the overlap matrices after the tracking # and phase correction roots = [join(project_name, 'overlaps_{}'.format(i)) for i in range(enumerate_from, number_of_frames + enumerate_from)] paths_corrected_overlaps = [join(r, 'mtx_sji_t0_corrected') for r in roots] # Paths inside the HDF5 to the array containing the tracking of the # unavoided crossings path_swaps = join(project_name, 'swaps') # Compute the corrected overlaps if not avaialable in the HDF5 if not is_data_in_hdf5(path_hdf5, paths_corrected_overlaps[0]): # Compute the dimension of the coupling matrix mtx_0 = retrieve_hdf5_data(path_hdf5, paths_overlaps[0]) _, dim = mtx_0.shape # Read all the Overlaps overlaps = np.stack([retrieve_hdf5_data(path_hdf5, ps) for ps in paths_overlaps]) # Number of couplings to compute nCouplings = overlaps.shape[0] # Compute the unavoided crossing using the Overlap matrix # and correct the swaps between Molecular Orbitals logger.debug("Computing the Unavoided crossings, " "Tracking the crossings between MOs") overlaps, swaps = track_unavoided_crossings(overlaps, nHOMO) # Compute all the phases taking into account the unavoided crossings logger.debug("Computing the phases of the MOs") mtx_phases = compute_phases(overlaps, nCouplings, dim) # Fixed the phases of the whole set of overlap matrices fixed_phase_overlaps = correct_phases(overlaps, mtx_phases) # Store corrected overlaps in the HDF5 store_arrays_in_hdf5(path_hdf5, paths_corrected_overlaps, fixed_phase_overlaps) # Store the Swaps tracking the crossing store_arrays_in_hdf5(path_hdf5, path_swaps, swaps, dtype=np.int32) else: # Read the corrected overlaps and the swaps from the HDF5 fixed_phase_overlaps = np.stack( retrieve_hdf5_data(path_hdf5, paths_corrected_overlaps)) swaps = retrieve_hdf5_data(path_hdf5, path_swaps) return fixed_phase_overlaps, swaps
def compute_excited_states_tddft(config: dict, path_MOs: list, dict_input: dict): """ Compute the excited states properties (energy and coefficients) for a given `mo_index_range` using the `tddft` method and `xc_dft` exchange functional. """ logger.info("Reading energies and mo coefficients") # type of calculation energy, c_ao = retrieve_hdf5_data(config.path_hdf5, path_MOs) # Number of virtual orbitals nocc = config.active_space[0] nvirt = config.active_space[1] dict_input.update({"energy": energy, "c_ao": c_ao, "nocc": nocc, "nvirt": nvirt}) # Pass the molecule in Angstrom to the libint calculator copy_dict = DictConfig(dict_input.copy()) copy_dict["mol"] = change_mol_units(dict_input["mol"], factor=1/angs2au) # compute the multipoles if they are not stored multipoles = get_multipole_matrix(config, copy_dict, 'dipole') # read data from the HDF5 or calculate it on the fly dict_input["overlap"] = multipoles[0] # retrieve or compute the omega xia values omega, xia = get_omega_xia(config, dict_input) # add arrays to the dictionary dict_input.update({"multipoles": multipoles[1:], "omega": omega, "xia": xia}) return compute_oscillator_strengths( config, dict_input)
def get_omega_xia(config: dict, dict_input: dict): """ Search for the multipole_matrices, Omega and xia values in the HDF5, if they are not available compute and store them. """ tddft = config.tddft.lower() def compute_omega_xia(): if tddft == 'sing_orb': return compute_sing_orb(dict_input) else: return compute_std_aproximation(config, dict_input) # search data in HDF5 root = join(config.project_name, 'omega_xia', tddft, 'point_{}'.format(dict_input.i + config.enumerate_from)) paths_omega_xia = [join(root, x) for x in ("omega", "xia")] if is_data_in_hdf5(config.path_hdf5, paths_omega_xia): return tuple(retrieve_hdf5_data(config.path_hdf5, paths_omega_xia)) else: omega, xia = compute_omega_xia() store_arrays_in_hdf5(config.path_hdf5, paths_omega_xia[0], omega, dtype=omega.dtype) store_arrays_in_hdf5(config.path_hdf5, paths_omega_xia[1], xia, dtype=xia.dtype) return omega, xia
def lazy_couplings(config: dict, paths_overlaps: list) -> list: """ Compute the Nonadibatic coupling using a 3 point approximation. See: The Journal of Chemical Physics 137, 22A514 (2012); doi: 10.1063/1.4738960 or a 2Point approximation using an smoothing function: J. Phys. Chem. Lett. 2014, 5, 2351−2356; doi: 10.1021/jz5009449 Notice that the states can cross frequently due to unavoided crossing and such crossing must be track. see: J. Chem. Phys. 137, 014512 (2012); doi: 10.1063/1.4732536 """ if config.tracking: fixed_phase_overlaps, swaps = compute_the_fixed_phase_overlaps( paths_overlaps, config.path_hdf5, config.project_name, config.enumerate_from, config.nHOMO) else: # Do not track the crossings mtx_0 = retrieve_hdf5_data(config.path_hdf5, paths_overlaps[0]) _, dim = mtx_0.shape overlaps = np.stack( retrieve_hdf5_data(config.path_hdf5, paths_overlaps)) nOverlaps, nOrbitals, _ = overlaps.shape swaps = np.tile(np.arange(nOrbitals), (nOverlaps + 1, 1)) mtx_phases = compute_phases(overlaps, nOverlaps, dim) fixed_phase_overlaps = correct_phases(overlaps, mtx_phases) # Write the overlaps in text format logger.debug("Writing down the overlaps in ascii format") write_overlaps_in_ascii(fixed_phase_overlaps) # Compute the couplings using either the levine method # or the 3Points approximation coupling_algorithms = {'levine': (calculate_couplings_levine, 1), '3points': (calculate_couplings_3points, 2)} # Choose an algorithm to compute the couplings fun_coupling, step = coupling_algorithms[config["algorithm"]] config["fun_coupling"] = fun_coupling # Number of couplings to compute nCouplings = fixed_phase_overlaps.shape[0] - step + 1 couplings = [calculate_couplings(config, i, fixed_phase_overlaps) for i in range(nCouplings)] return swaps, couplings
def search_multipole_in_hdf5(path_hdf5: str, path_multipole_hdf5: str, multipole: str): """ Search if the multipole is already store in the HDFt """ if is_data_in_hdf5(path_hdf5, path_multipole_hdf5): print("retrieving multipole: {} from the hdf5".format(multipole)) return retrieve_hdf5_data(path_hdf5, path_multipole_hdf5) else: print("computing multipole: {}".format(multipole)) return None
def check_properties(path_test_hdf5): """ Check that the tensor stored in the HDF5 are correct. """ dipole_matrices = retrieve_hdf5_data( path_test_hdf5, 'Cd/multipole/point_0/dipole') # The diagonals of each component of the matrix must be zero # for a single atom diagonals = np.sum([np.diag(dipole_matrices[n + 1]) for n in range(3)]) assert abs(diagonals) < 1e-16
def read_overlap_data(config: dict, mo_paths: list) -> Tuple: """ Read the Molecular orbital coefficients and the transformation matrix """ mos = retrieve_hdf5_data(config.path_hdf5, mo_paths) # Extract a subset of molecular orbitals to compute the coupling lowest, highest = compute_range_orbitals(mos[0], config.nHOMO, config.mo_index_range) css0, css1 = tuple(map(lambda xs: xs[:, lowest: highest], mos)) return css0, css1
def read_overlap_data(config: dict, mo_paths: list) -> tuple: """ Read the Molecular orbital coefficients and the transformation matrix """ mos = retrieve_hdf5_data(config.path_hdf5, mo_paths) # Extract a subset of molecular orbitals to compute the coupling lowest, highest = compute_range_orbitals(config) css0, css1 = tuple(map(lambda xs: xs[:, lowest: highest], mos)) return css0, css1
def read_swaps(path_hdf5: str, project_name: str) -> Matrix: """ Read the crossing tracking for the Molecular orbital """ path_swaps = join(project_name, 'swaps') if search_data_in_hdf5(path_hdf5, path_swaps): return retrieve_hdf5_data(path_hdf5, path_swaps) else: msg = """There is not a tracking file called: {} This file is automatically created when running the worflow_coupling simulations""".format(path_swaps) raise RuntimeError(msg)
def read_swaps(path_hdf5: str, project_name: str) -> Matrix: """ Read the crossing tracking for the Molecular orbital """ path_swaps = join(project_name, 'swaps') if is_data_in_hdf5(path_hdf5, path_swaps): return retrieve_hdf5_data(path_hdf5, path_swaps) else: msg = """There is not a tracking file called: {} This file is automatically created when running the worflow_coupling simulations""".format(path_swaps) raise RuntimeError(msg)
def check_properties(path_test_hdf5): """ Test if the coupling coupling by the Levine method is correct """ # Paths to all the arrays to test path_swaps = join(project_name, 'swaps') name_Sji = 'overlaps_{}/mtx_sji_t0' name_Sji_fixed = 'overlaps_{}/mtx_sji_t0_corrected' path_overlaps = [join(project_name, name_Sji.format(i)) for i in range(4)] path_fixed_overlaps = [ join(project_name, name_Sji_fixed.format(i)) for i in range(4) ] path_couplings = [ join(project_name, 'coupling_{}'.format(i)) for i in range(4) ] # Define partial func fun_original = partial(stack_retrieve, path_original_hdf5) fun_test = partial(stack_retrieve, path_test_hdf5) # Read data from the HDF5 swaps_original = retrieve_hdf5_data(path_original_hdf5, path_swaps) swaps_test = retrieve_hdf5_data(path_test_hdf5, path_swaps) overlaps_original = fun_original(path_overlaps) overlaps_test = fun_test(path_overlaps) fixed_overlaps_original = fun_original(path_fixed_overlaps) fixed_overlaps_test = fun_test(path_fixed_overlaps) css_original = fun_original(path_couplings) css_test = fun_test(path_couplings) # Test data b1 = np.allclose(swaps_original, swaps_test) b2 = np.allclose(overlaps_original, overlaps_test) b3 = np.allclose(fixed_overlaps_original, fixed_overlaps_test) b4 = np.allclose(css_original, css_test) assert all((b1, b2, b3, b4))
def lazy_couplings(paths_overlaps: List, path_hdf5: str, project_name: str, enumerate_from: int, nHOMO: int, dt: float, tracking: bool, algorithm='levine') -> List: """ Compute the Nonadibatic coupling using a 3 point approximation. See: The Journal of Chemical Physics 137, 22A514 (2012); doi: 10.1063/1.4738960 or a 2Point approximation using an smoothing function: J. Phys. Chem. Lett. 2014, 5, 2351−2356; doi: 10.1021/jz5009449 Notice that the states can cross frequently due to unavoided crossing and such crossing must be track. see: J. Chem. Phys. 137, 014512 (2012); doi: 10.1063/1.4732536 """ if tracking: fixed_phase_overlaps, swaps = compute_the_fixed_phase_overlaps( paths_overlaps, path_hdf5, project_name, enumerate_from, nHOMO) else: # Do not track the crossings fixed_phase_overlaps = np.stack( retrieve_hdf5_data(path_hdf5, paths_overlaps)) nOverlaps, nOrbitals, _ = fixed_phase_overlaps.shape swaps = np.tile(np.arange(nOrbitals), (nOverlaps + 1, 1)) # Compute the couplings using either the levine method # or the 3Points approximation coupling_algorithms = { 'levine': (calculate_couplings_levine, 1), '3points': (calculate_couplings_3points, 2) } # Choose an algorithm to compute the couplings fun_coupling, step = coupling_algorithms[algorithm] # Number of couplings to compute nCouplings = fixed_phase_overlaps.shape[0] - step + 1 # time in atomic units dt_au = dt * femtosec2au couplings = [ calculate_couplings(fun_coupling, algorithm, i, project_name, fixed_phase_overlaps, path_hdf5, enumerate_from, dt_au) for i in range(nCouplings) ] return swaps, couplings
def test_cube(): """ Test the density compute to create a cube file. """ if not os.path.exists(scratch_path): os.makedirs(scratch_path) # Overlap matrix in cartesian coordinates basisname = "DZVP-MOLOPT-SR-GTH" # Read coordinates molecule = change_mol_units(readXYZ(path_xyz)) # String representation of the molecule geometries = split_file_geometries(path_xyz) try: shutil.copy(path_original_hdf5, path_test_hdf5) # Contracted Gauss functions dictCGFs = create_dict_CGFs(path_test_hdf5, basisname, molecule) dict_global_norms = compute_normalization_sphericals(dictCGFs) # Compute the transformation matrix and store it in the HDF5 with h5py.File(path_test_hdf5, 'r') as f5: transf_mtx = calc_transf_matrix( f5, molecule, basisname, dict_global_norms, 'cp2k') path_transf_mtx = join(project_name, 'trans_mtx') store_arrays_in_hdf5( path_test_hdf5, path_transf_mtx, transf_mtx) # voxel size and Number of steps grid_data = GridCube(0.300939, 80) rs = workflow_compute_cubes( 'cp2k', project_name, package_args, path_time_coeffs=None, grid_data=grid_data, guess_args=None, geometries=geometries, dictCGFs=dictCGFs, calc_new_wf_guess_on_points=[], path_hdf5=path_test_hdf5, enumerate_from=0, package_config=None, traj_folders=None, work_dir=scratch_path, basisname=basisname, hdf5_trans_mtx=path_transf_mtx, nHOMO=6, orbitals_range=(6, 6), ignore_warnings=False) xs = retrieve_hdf5_data(path_test_hdf5, rs)[0] np.save("grid_density", xs) finally: # Remove intermediate results shutil.rmtree(scratch_path)
def write_data(i): j = i + config.enumerate_from path_coupling = path_couplings[i] css = retrieve_hdf5_data(config.path_hdf5, path_coupling) # Extract the energy values at time t # The first coupling is compute at time t + dt # Then I'm shifting the energies dt to get the correct value energies_t0 = retrieve_hdf5_data(config.path_hdf5, mo_paths_hdf5[i][0]) energies_t1 = retrieve_hdf5_data(config.path_hdf5, mo_paths_hdf5[i + 1][0]) # Return the average between time t and t + dt energies = np.average((energies_t0, energies_t1), axis=0) # Print Energies in the range given by the user if all(x is not None for x in [nHOMO, mo_index_range]): lowest = nHOMO - mo_index_range[0] highest = nHOMO + mo_index_range[1] energies = energies[lowest: highest] # Swap the energies of the states that are crossing energies = energies[swaps[i + 1]] # FileNames path_dir_results = config.path_hamiltonians file_ham_im = join(path_dir_results, 'Ham_{}_im'.format(j)) file_ham_re = join(path_dir_results, 'Ham_{}_re'.format(j)) # convert to Rydbergs ham_im = 2.0 * css ham_re = np.diag(2.0 * energies) write_pyxaid_format(ham_im, file_ham_im) write_pyxaid_format(ham_re, file_ham_re) return (file_ham_im, file_ham_re)
def write_data(i): j = i + config.enumerate_from path_coupling = path_couplings[i] css = retrieve_hdf5_data(config.path_hdf5, path_coupling) # Extract the energy values at time t # The first coupling is compute at time t + dt # Then I'm shifting the energies dt to get the correct value energies_t0 = retrieve_hdf5_data(config.path_hdf5, mo_paths_hdf5[i][0]) energies_t1 = retrieve_hdf5_data( config.path_hdf5, mo_paths_hdf5[i + 1][0]) # Return the average between time t and t + dt energies = np.average((energies_t0, energies_t1), axis=0) # Print Energies in the range given by the user if all(x is not None for x in [nHOMO, mo_index_range]): lowest, highest = compute_range_orbitals(config) energies = energies[lowest: highest] # Swap the energies of the states that are crossing energies = energies[swaps[i + 1]] # FileNames path_dir_results = config.path_hamiltonians file_ham_im = join(path_dir_results, f'Ham_{i}_im') file_ham_re = join(path_dir_results, f'Ham_{j}_re') # convert to Rydbergs ham_im = 2.0 * css ham_re = np.diag(2.0 * energies) write_pyxaid_format(ham_im, file_ham_im) write_pyxaid_format(ham_re, file_ham_re) return (file_ham_im, file_ham_re)
def check_couplings(config: dict, tmp_hdf5: str) -> None: """ Check that the couplings have meaningful values """ def create_paths(keyword: str) -> list: return ['{}/{}_{}'.format(config.project_name, keyword, x) for x in range(len(config.geometries) - 1)] overlaps = create_paths('overlaps') couplings = create_paths('coupling') # Check that couplings and overlaps exists assert is_data_in_hdf5(tmp_hdf5, overlaps) assert is_data_in_hdf5(tmp_hdf5, couplings) # All the elements are different of inifinity or nan tensor_couplings = np.stack(retrieve_hdf5_data(tmp_hdf5, couplings)) assert np.isfinite(tensor_couplings).all()
def compute_photoexcitation(path_hdf5: str, time_dependent_coeffs: Matrix, paths_fragment_overlaps: List, map_index_pyxaid_hdf5: Matrix, swaps: Matrix, n_points: int, pyxaid_iconds: List, dt_au: float) -> List: """ :param i: Electron transfer rate at time i * dt :param path_hdf5: Path to the HDF5 file that contains the numerical results. :param geometries: list of 3 contiguous molecular geometries :param time_depend_paths: mean value of the time dependent coefficients computed with PYXAID. param fragment_overlaps: Tensor containing 3 overlap matrices corresponding with the `geometries`. :param map_index_pyxaid_hdf5: map from PYXAID excitation to the indices i,j of the molecular orbitals stored in the HDF5. :param swaps: Matrix containing the crossing between the MOs during the molecular dynamics. :param n_points: Number of frames to compute the ETR. :param pyxaid_iconds: List of initial conditions :param dt_au: Delta time in atomic units :returns: promise to path to the Coupling inside the HDF5. """ msg = "Computing the photo-excitation rate for the molecular fragments" logger.info(msg) results = [] for paths_overlaps in paths_fragment_overlaps: overlaps = np.stack(retrieve_hdf5_data(path_hdf5, paths_overlaps)) # Track the crossing between MOs for k, mtx in enumerate(np.rollaxis(overlaps, 0)): overlaps[k] = mtx[:, swaps[k]][swaps[k]] etr = np.stack( np.array([ photo_excitation_rate(overlaps[i:i + 3], time_dependent_coeffs[ j, i:i + 3], map_index_pyxaid_hdf5, dt_au) for i in range(n_points) ]) for j in range(len(pyxaid_iconds))) etr = np.mean(etr, axis=0) results.append(etr) return np.stack(results)
def write_overlap_densities(path_hdf5: str, paths_fragment_overlaps: List, dt: int=1): """ Write the diagonal of the overlap matrices """ logger.info("writing densities in human readable format") for k, paths_overlaps in enumerate(paths_fragment_overlaps): overlaps = np.stack(retrieve_hdf5_data(path_hdf5, paths_overlaps)) # time frame frames = overlaps.shape[0] ts = np.arange(1, frames + 1).reshape(frames, 1) * dt # Diagonal of the 3D-tensor densities = np.diagonal(overlaps, axis1=1, axis2=2) data = np.hstack((ts, densities)) # Save data in human readable format file_name = 'densities_fragment_{}.txt'.format(k) np.savetxt(file_name, data, fmt='{:^3}'.format('%e'))
def compute_excited_states_tddft(config: dict, path_MOs: list, dict_input: dict): """ Compute the excited states properties (energy and coefficients) for a given `mo_index_range` using the `tddft` method and `xc_dft` exchange functional. """ logger.info("Reading energies and mo coefficients") # type of calculation energy, c_ao = retrieve_hdf5_data(config.path_hdf5, path_MOs) # Number of virtual orbitals nocc = config.active_space[0] nvirt = config.active_space[1] dict_input.update({ "energy": energy, "c_ao": c_ao, "nocc": nocc, "nvirt": nvirt }) # Pass the molecule in Angstrom to the libint calculator copy_dict = DictConfig(dict_input.copy()) copy_dict["mol"] = change_mol_units(dict_input["mol"], factor=1 / angs2au) # compute the multipoles if they are not stored multipoles = get_multipole_matrix(config, copy_dict, 'dipole') # read data from the HDF5 or calculate it on the fly dict_input["overlap"] = multipoles[0] # retrieve or compute the omega xia values omega, xia = get_omega_xia(config, dict_input) # add arrays to the dictionary dict_input.update({ "multipoles": multipoles[1:], "omega": omega, "xia": xia }) return compute_oscillator_strengths(config, dict_input)
def stack_retrieve(path_hdf5, path_prop): """ Retrieve a list of Numpy arrays and create a tensor out of it """ return np.stack(retrieve_hdf5_data(path_hdf5, path_prop))
def calcOscillatorStrenghts(i: int, project_name: str, mo_paths_hdf5: str, dictCGFs: Dict, atoms: List, path_hdf5: str, hdf5_trans_mtx: str = None, initial_states: Vector = None, final_states: Matrix = None): """ Use the Molecular orbital Energies and Coefficients to compute the oscillator_strength. :param i: time frame :param project_name: Folder name where the computations are going to be stored. :param mo_paths_hdf5: Path to the MO coefficients and energies in the HDF5 file. :paramter dictCGFS: Dictionary from Atomic Label to basis set. :type dictCGFS: Dict String [CGF], CGF = ([Primitives], AngularMomentum), Primitive = (Coefficient, Exponent) :param atoms: Molecular geometry. :type atoms: [namedtuple("AtomXYZ", ("symbol", "xyz"))] :param path_hdf5: Path to the HDF5 file that contains the numerical results. :param hdf5_trans_mtx: path to the transformation matrix in the HDF5 file. :param initial_states: List of the initial Electronic states. :type initial_states: [Int] :param final_states: List containing the sets of possible electronic states. :type final_states: [[Int]] """ # Energy and coefficients at time t es, coeffs = retrieve_hdf5_data(path_hdf5, mo_paths_hdf5[i]) # If the MO orbitals are given in Spherical Coordinates transform then to # Cartesian Coordinates. if hdf5_trans_mtx is not None: trans_mtx = retrieve_hdf5_data(path_hdf5, hdf5_trans_mtx) logger.info("Computing the oscillator strength at time: {}".format(i)) # Overlap matrix # Origin of the dipole rc = compute_center_of_mass(atoms) # Dipole matrix element in spherical coordinates path_dipole_matrices = join(project_name, 'point_{}'.format(i), 'dipole_matrices') if search_data_in_hdf5(path_hdf5, path_dipole_matrices): mtx_integrals_spher = retrieve_hdf5_data(path_hdf5, path_dipole_matrices) else: # Compute the Dipole matrices and store them in the HDF5 mtx_integrals_spher = calcDipoleCGFS(atoms, dictCGFs, rc, trans_mtx) store_arrays_in_hdf5(path_hdf5, path_dipole_matrices, mtx_integrals_spher) oscillators = [ compute_oscillator_strength(rc, atoms, dictCGFs, es, coeffs, mtx_integrals_spher, initialS, fs) for initialS, fs in zip(initial_states, final_states) ] return oscillators
def compute_the_fixed_phase_overlaps(paths_overlaps: List, path_hdf5: str, project_name: str, enumerate_from: int, nHOMO: int) -> Tuple: """ First track the unavoided crossings between Molecular orbitals and finally correct the phase for the whole trajectory. """ number_of_frames = len(paths_overlaps) # Pasth to the overlap matrices after the tracking # and phase correction roots = [ join(project_name, 'overlaps_{}'.format(i)) for i in range(enumerate_from, number_of_frames + enumerate_from) ] paths_corrected_overlaps = [join(r, 'mtx_sji_t0_corrected') for r in roots] # Paths inside the HDF5 to the array containing the tracking of the # unavoided crossings path_swaps = join(project_name, 'swaps') # Compute the corrected overlaps if not avaialable in the HDF5 if not search_data_in_hdf5(path_hdf5, paths_corrected_overlaps[0]): # Compute the dimension of the coupling matrix mtx_0 = retrieve_hdf5_data(path_hdf5, paths_overlaps[0]) _, dim = mtx_0.shape # Read all the Overlaps overlaps = np.stack( [retrieve_hdf5_data(path_hdf5, ps) for ps in paths_overlaps]) # Number of couplings to compute nCouplings = overlaps.shape[0] # Compute the unavoided crossing using the Overlap matrix # and correct the swaps between Molecular Orbitals logger.debug("Computing the Unavoided crossings, " "Tracking the crossings between MOs") overlaps, swaps = track_unavoided_crossings(overlaps, nHOMO) # Compute all the phases taking into account the unavoided crossings logger.debug("Computing the phases of the MOs") mtx_phases = compute_phases(overlaps, nCouplings, dim) # Fixed the phases of the whole set of overlap matrices fixed_phase_overlaps = correct_phases(overlaps, mtx_phases) # Store corrected overlaps in the HDF5 store_arrays_in_hdf5(path_hdf5, paths_corrected_overlaps, fixed_phase_overlaps) # Store the Swaps tracking the crossing store_arrays_in_hdf5(path_hdf5, path_swaps, swaps, dtype=np.int32) else: # Read the corrected overlaps and the swaps from the HDF5 fixed_phase_overlaps = np.stack( retrieve_hdf5_data(path_hdf5, paths_corrected_overlaps)) swaps = retrieve_hdf5_data(path_hdf5, path_swaps) # Write the overlaps in text format logger.debug("Writing down the overlaps in ascii format") write_overlaps_in_ascii(fixed_phase_overlaps) return fixed_phase_overlaps, swaps
def compute_matrix_multipole(mol: List, config: Dict, multipole: str) -> Matrix: """ Compute the some `multipole` matrix: overlap, dipole, etc. for a given geometry `mol`. Compute the Multipole matrix in cartesian coordinates and expand it to a matrix and finally convert it to spherical coordinates. :returns: Matrix with entries <ψi | x y z | ψj> """ path_hdf5 = config['path_hdf5'] runner = config['runner'] # Compute the number of cartesian basis functions dictCGFs = config['dictCGFs'] n_cart_funcs = np.sum(np.stack(len(dictCGFs[at.symbol]) for at in mol)) # Compute the transformation matrix from cartesian to spherical transf_mtx = retrieve_hdf5_data(path_hdf5, config['hdf5_trans_mtx']) transf_mtx = sparse.csr_matrix(transf_mtx) transpose = transf_mtx.transpose() if multipole == 'overlap': rs = calcMtxOverlapP(mol, dictCGFs, runner) mtx_overlap = triang2mtx( rs, n_cart_funcs) # there are 1452 Cartesian basis CGFs matrix_multipole = transf_mtx.dot( sparse.csr_matrix.dot(mtx_overlap, transpose)) else: rc = compute_center_of_mass(mol) exponents = { 'dipole': [{ 'e': 1, 'f': 0, 'g': 0 }, { 'e': 0, 'f': 1, 'g': 0 }, { 'e': 0, 'f': 0, 'g': 1 }], 'quadrupole': [{ 'e': 2, 'f': 0, 'g': 0 }, { 'e': 0, 'f': 2, 'g': 0 }, { 'e': 0, 'f': 0, 'g': 2 }] } mtx_integrals_triang = tuple( calcMtxMultipoleP(mol, dictCGFs, runner, rc, **kw) for kw in exponents[multipole]) mtx_integrals_cart = tuple( triang2mtx(xs, n_cart_funcs) for xs in mtx_integrals_triang) matrix_multipole = np.stack( transf_mtx.dot(sparse.csr_matrix.dot(x, transpose)) for x in mtx_integrals_cart) return matrix_multipole