import sys import numpy as np config.log.stdout = 0 # This test calculates the vibrational fine structure of the S_0 -> S_1 transition in anthracene. molfile = "anthracene.xyz" excit = 1 # Common settings for all DFTB calculations: comin = Settings() comin.input.DFTB.resourcesdir = "DFTB.org/3ob-freq-1-2" comin.input.DFTB.model = "DFTB3" # ========= auxilliary functions ================================================================== def get_total_energy(results): nprop = results.readrkf("Properties", "nEntries", file="dftb") for i in range(1, nprop + 1): if results.readrkf("Properties", "Subtype(%i)" % i, file="dftb").strip() == "DFTB Final Energy": return results.readrkf("Properties", "Value(%i)" % i, file="dftb") return None def get_zero_point_energy(results): freqs = results.readrkf("Vibrations", "Frequencies[cm-1]", file="dftb") if isinstance(freqs, list): return Units.convert(0.5 * sum(freqs), "cm^-1", "Hartree") else: return Units.convert(0.5 * freqs, "cm^-1", "Hartree") def extract_spectrum(fcf_results): return np.array(fcf_results.readkf("Fcf", "spectrum")).reshape(2, -1).transpose() # ========= STEP 1: Ground state ================================================================== # Optimize ground state geometry: gs_mol_unoptimized = Molecule(filename=molfile) gs_go = AMSJob(name="gs_go", molecule=gs_mol_unoptimized, settings=comin) gs_go.settings.input.ams.Task = "GeometryOptimization" gs_go.settings.input.ams.GeometryOptimization.convergence = "Gradients=1.0e-5" gs_go_results = gs_go.run() if not gs_go.check(): print("ERROR: Ground state optimization crashed") sys.exit(1) if gs_go_results.grep_output("Optimization Did Not Converge"): print("ERROR: Ground state optimization did not converge") sys.exit(1) gs_mol_optimized = gs_go_results.get_molecule("Molecule") # Calculate frequencies and normal modes of the ground state: gs_freq = AMSJob(name="gs_freq", molecule=gs_mol_optimized, settings=comin) gs_freq.settings.input.ams.Task = "SinglePoint" gs_freq.settings.input.ams.properties.NormalModes = "true" gs_freq.settings.input.ams.NumericalDifferentiation.Parallel.nCoresPerGroup = 1 gs_freq_results = gs_freq.run() if not gs_freq.check(): print("ERROR: Ground state frequency calculation crashed") sys.exit(1) # Calculate vertical excitations: gs_excit = AMSJob(name="gs_excit", molecule=gs_mol_optimized, settings=comin) gs_excit.settings.input.ams.Task = "SinglePoint" gs_excit.settings.input.DFTB.properties.excitations.tddftb.calc = "singlet" gs_excit.settings.input.DFTB.properties.excitations.tddftb.lowest = excit + 9 gs_excit.settings.input.DFTB.properties.excitations.tddftb["print"] = "evcontribs" gs_excit_results = gs_excit.run() if not gs_excit.check(): print("ERROR: Ground state excitations calculation crashed") sys.exit(1) # Print ground state energies: print("Energies in the ground state equilibrium geometry:") E_DFTB_RGS = get_total_energy(gs_excit_results) E_ZPE_RGS = get_zero_point_energy(gs_freq_results) Delta_RGS = gs_excit_results.readrkf("Excitations SS A", "excenergies", file="dftb")[excit - 1] E_GS = E_DFTB_RGS + E_ZPE_RGS print(" E_DFTB(R_GS) = %f eV" % (Units.convert(E_DFTB_RGS, "Hartree", "eV"))) print(" E_ZPE(R_GS) = %f eV" % (Units.convert(E_ZPE_RGS, "Hartree", "eV"))) print(" E_GS = %f eV" % (Units.convert(E_GS, "Hartree", "eV"))) # ========= STEP 2: Excited state ================================================================= # Optimize the excited state geometry: ex_go = AMSJob(name="ex_go", molecule=gs_mol_optimized, settings=comin) ex_go.settings.input.ams.Task = "GeometryOptimization" ex_go.settings.input.ams.GeometryOptimization.convergence = "Gradients=1.0e-5" ex_go.settings.input.DFTB.properties.excitations.tddftb.calc = "singlet" ex_go.settings.input.DFTB.properties.excitations.tddftb.lowest = excit ex_go.settings.input.DFTB.properties.excitations.tddftb["print"] = "evcontribs" ex_go.settings.input.DFTB.properties.excitations.tddftbgradients.excitation = excit ex_go.settings.input.DFTB.properties.excitations.tddftbgradients.eigenfollow = "true" ex_go.settings.input.ams.log.info = "TDDFTBExcitationFollowerModule" ex_go_results = ex_go.run() if not ex_go.check(): print("ERROR: Excited state optimization crashed") sys.exit(1) if ex_go_results.grep_output("Optimization Did Not Converge"): print("ERROR: Excited state optimization did not converge") sys.exit(1) ex_mol_optimized = ex_go_results.get_molecule("Molecule") # Check if the potential energy surface was switched during the optimization: # (This happens if the optimizer goes through a conical intersection.) PES_switches = ex_go_results.grep_file("ams.log", "TD-DFTB Eigenfollower switching PES:") if PES_switches: newexcit = int(PES_switches[-1].split()[-1]) print("PES switched during EXGO!!! %i -> %i" % (excit, newexcit)) else: newexcit = excit # Calculate frequencies and normal modes of the excited state: ex_freq = AMSJob(name="ex_freq", molecule=ex_mol_optimized, settings=comin) ex_freq.settings.input.ams.Task = "SinglePoint" ex_freq.settings.input.ams.properties.NormalModes = "true" ex_freq.settings.input.ams.NumericalDifferentiation.Parallel.nCoresPerGroup = 1 ex_freq.settings.input.DFTB.properties.excitations.tddftb.calc = "singlet" ex_freq.settings.input.DFTB.properties.excitations.tddftb.lowest = newexcit ex_freq.settings.input.DFTB.properties.excitations.tddftb["print"] = "evcontribs" ex_freq.settings.input.DFTB.properties.excitations.tddftbgradients.excitation = newexcit ex_freq_results = ex_freq.run() if not ex_freq.check(): print("ERROR: Excited state frequency calculation crashed") sys.exit(1) # Calculate vertical excitations in excited state geometry: ex_excit = AMSJob(name="ex_excit", molecule=ex_mol_optimized, settings=comin) ex_excit.settings.input.ams.Task = "SinglePoint" ex_excit.settings.input.DFTB.properties.excitations.tddftb.calc = "singlet" ex_excit.settings.input.DFTB.properties.excitations.tddftb.lowest = newexcit + 9 ex_excit.settings.input.DFTB.properties.excitations.tddftb["print"] = "evcontribs" ex_excit_results = ex_excit.run() if not ex_excit.check(): print("ERROR: Excited state geometry excitations calculation crashed") sys.exit(1) # Print excited state energies: print("Energies in the excited state equilibrium geometry:") E_DFTB_REX = get_total_energy(ex_excit_results) E_ZPE_REX = get_zero_point_energy(ex_freq_results) Delta_REX = ex_excit_results.readrkf("Excitations SS A", "excenergies", file="dftb")[excit - 1] E_EX = E_DFTB_REX + E_ZPE_REX + Delta_REX print(" E_DFTB(R_EX) = %f eV" % (Units.convert(E_DFTB_REX, "Hartree", "eV"))) print(" E_ZPE(R_EX) = %f eV" % (Units.convert(E_ZPE_REX, "Hartree", "eV"))) print(" Delta(R_EX) = %f eV" % (Units.convert(Delta_REX, "Hartree", "eV"))) print(" E_EX = %f eV" % (Units.convert(E_EX, "Hartree", "eV"))) # Print excitation energies: print("Excitation energies:") print(" Delta(R_GS) = %f eV" % (Units.convert(Delta_RGS, "Hartree", "eV"))) print(" E_0-0 = %f eV" % (Units.convert(E_EX - E_GS, "Hartree", "eV"))) print(" Diff = %f eV" % (Units.convert(Delta_RGS - (E_EX - E_GS), "Hartree", "eV"))) # ========= STEP 3: Vibrational fine structure with the FCF program =============================== # Settings for the FCF program fcfin = Settings() fcfin.input.spectrum.spcmin = "0.0" fcfin.input.spectrum.spcmax = "5000.0" fcfin.input.spectrum.spclen = "501" fcfin.input.spectrum.lineshape = "Stick" fcfin.input.numericalquality = "Basic" fcfin.input.translate = True fcfin.input.rotate = True # Calculate vibrational fine structure fcf = FCFJob( name="fcf", settings=fcfin, inputjob1=gs_freq_results.rkfpath(file="dftb"), inputjob2=ex_freq_results.rkfpath(file="dftb"), ) fcf_results = fcf.run() if not fcf.check(): print("ERROR: FCF calculation failed") sys.exit(1) # Extract and print the spectrum: spectrum = extract_spectrum(fcf_results) np.set_printoptions(formatter={"float": " {: 0.8f} ".format}, threshold=1e6) print("Vibrational fine structure:") print("Energy [cm^-1] Intensity") print(spectrum)