Example: Vibration resolved electronic spectrum: plams

Download GOFREQ_LR-TDDFTB_anthracene_S0S1fcf.run

#!/bin/sh

cp $TEST_DIRECTORY/GOFREQ_LR-TDDFTB_anthracene_S0S1fcf.plms .
cp $TEST_DIRECTORY/anthracene.xyz .

$AMSBIN/plams GOFREQ_LR-TDDFTB_anthracene_S0S1fcf.plms

Download GOFREQ_LR-TDDFTB_anthracene_S0S1fcf.plms

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)