Franck-Condon Vibronic DOS with ADF¶
This example uses ADF to model the density of states involved in charge transfer between two systems with the AH-FC method, here for an NO₂🞄 radical and an NO₂⁻ anion.
Initial Imports¶
Import PLAMS components and set up to run jobs in tandem.
from scm.inputs import ADF, AMS
from scm.plams import Settings, AMSJob, FCFJob, config, JobRunner, view
from scm.plams.recipes.fcf_dos import FCFDOS
from scm.base import ChemicalSystem
config.default_jobrunner.parallel = JobRunner(parallel=True, maxjobs=2)
Setup Molecules¶
Create the NO2 molecules using pre-optimized geometries (usually the geometry optimization step would come first).
no2_radical = ChemicalSystem()
no2_radical.add_atom("N", coords=(0.0, 0.0, -0.01857566))
no2_radical.add_atom("O", coords=(0.0, 1.09915770, -0.49171967))
no2_radical.add_atom("O", coords=(0.0, -1.09915770, -0.49171967))
view(no2_radical, width=200, height=200, direction="along_x", guess_bonds=True)
no2_anion = ChemicalSystem()
no2_anion.add_atom("N", coords=(0.0, 0.0, 0.12041))
no2_anion.add_atom("O", coords=(0.0, 1.070642, -0.555172))
no2_anion.add_atom("O", coords=(0.0, -1.070642, -0.555172))
no2_anion.charge = -1
view(no2_anion, width=200, height=200, direction="along_x", guess_bonds=True)
Calculate Vibrational Frequencies¶
Create the settings objects for the ADF donor/acceptor vibrational frequencies calculations. Run the calculations in parallel.
settings_freq = AMS(Task="SinglePoint")
settings_freq.Properties.NormalModes = True
settings_freq.Engine = ADF()
settings_freq.Engine.Symmetry = "NoSym"
settings_freq.Engine.Basis.Type = "DZP"
settings_freq.Engine.Basis.Core = "None"
settings_freq.Engine.XC.LDA = "SCF VWN"
settings_freq.Engine.Title = "Vibrational frequencies"
settings_freq_radical = settings_freq.copy()
settings_freq_radical.Engine.SpinPolarization = 1
settings_freq_radical.Engine.Unrestricted = True
settings_freq_anion = settings_freq.copy()
freq_job_radical = AMSJob(molecule=no2_radical, settings=settings_freq_radical, name="fsradical")
freq_job_anion = AMSJob(molecule=no2_anion, settings=settings_freq_anion, name="fsanion")
freq_results = (freq_job_radical.run(), freq_job_anion.run())
[12.10|12:48:48] JOB fsradical STARTED
[12.10|12:48:48] JOB fsanion STARTED
[12.10|12:48:48] JOB fsradical RUNNING
[12.10|12:48:48] JOB fsanion RUNNING
rkf = freq_results[0].rkfpath(file="adf")
!amsspectra {rkf}
[12.10|12:48:49] Waiting for job fsradical to finish
[12.10|12:48:52] JOB fsanion FINISHED
[12.10|12:48:52] JOB fsanion SUCCESSFUL
[12.10|12:48:53] JOB fsradical FINISHED
[12.10|12:48:53] JOB fsradical SUCCESSFUL
Calculate Vibronic Spectra¶
Use Frank-Condon jobs to calculate the vibronic spectra.
def fcf_job(state1, state2, spctype, name):
settings_fcf = Settings()
settings_fcf.input.spectrum.type = spctype
settings_fcf.input.state1 = state1
settings_fcf.input.state2 = state2
return FCFJob(inputjob1=state1, inputjob2=state2, settings=settings_fcf, name=name)
freq_radical = freq_results[0].rkfpath(file="adf")
freq_anion = freq_results[1].rkfpath(file="adf")
fc_abs = fcf_job(freq_radical, freq_anion, "absorption", "fcfabs")
fc_emi = fcf_job(freq_anion, freq_radical, "emission", "fcfemi")
fc_results = (fc_abs.run(), fc_emi.run())
[12.10|12:48:59] JOB fcfabs STARTED
[12.10|12:48:59] JOB fcfemi STARTED
[12.10|12:48:59] JOB fcfabs RUNNING
Calculate Density of States¶
Calculate the DOS by computing the overlap of the absorption and emission FCF spectra.
job = FCFDOS(fc_results[0].kfpath(), fc_results[1].kfpath(), 10000.0, 10000.0)
dos = job.dos()
[12.10|12:48:59] Waiting for job fcfabs to finish
[12.10|12:48:59] JOB fcfemi RUNNING
[12.10|12:48:59] JOB fcfabs FINISHED
[12.10|12:48:59] JOB fcfabs SUCCESSFUL
[12.10|12:48:59] Waiting for job fcfemi to finish
[12.10|12:48:59] JOB fcfemi FINISHED
[12.10|12:48:59] JOB fcfemi SUCCESSFUL
print(f"The density of states is {dos:.8e}")
The density of states is 1.30090303e-08
See also¶
Python Script¶
#!/usr/bin/env python
# coding: utf-8
# ## Initial Imports
# Import PLAMS components and set up to run jobs in tandem.
from scm.inputs import ADF, AMS
from scm.plams import Settings, AMSJob, FCFJob, config, JobRunner, view
from scm.plams.recipes.fcf_dos import FCFDOS
from scm.base import ChemicalSystem
config.default_jobrunner.parallel = JobRunner(parallel=True, maxjobs=2)
# ## Setup Molecules
# Create the NO<sub>2</sub> molecules using pre-optimized geometries (usually the geometry optimization step would come first).
no2_radical = ChemicalSystem()
no2_radical.add_atom("N", coords=(0.0, 0.0, -0.01857566))
no2_radical.add_atom("O", coords=(0.0, 1.09915770, -0.49171967))
no2_radical.add_atom("O", coords=(0.0, -1.09915770, -0.49171967))
view(no2_radical, width=200, height=200, direction="along_x", guess_bonds=True, picture_path="picture1.png")
no2_anion = ChemicalSystem()
no2_anion.add_atom("N", coords=(0.0, 0.0, 0.12041))
no2_anion.add_atom("O", coords=(0.0, 1.070642, -0.555172))
no2_anion.add_atom("O", coords=(0.0, -1.070642, -0.555172))
no2_anion.charge = -1
view(no2_anion, width=200, height=200, direction="along_x", guess_bonds=True, picture_path="picture2.png")
# ## Calculate Vibrational Frequencies
# Create the settings objects for the ADF donor/acceptor vibrational frequencies calculations. Run the calculations in parallel.
settings_freq = AMS(Task="SinglePoint")
settings_freq.Properties.NormalModes = True
settings_freq.Engine = ADF()
settings_freq.Engine.Symmetry = "NoSym"
settings_freq.Engine.Basis.Type = "DZP"
settings_freq.Engine.Basis.Core = "None"
settings_freq.Engine.XC.LDA = "SCF VWN"
settings_freq.Engine.Title = "Vibrational frequencies"
settings_freq_radical = settings_freq.copy()
settings_freq_radical.Engine.SpinPolarization = 1
settings_freq_radical.Engine.Unrestricted = True
settings_freq_anion = settings_freq.copy()
freq_job_radical = AMSJob(molecule=no2_radical, settings=settings_freq_radical, name="fsradical")
freq_job_anion = AMSJob(molecule=no2_anion, settings=settings_freq_anion, name="fsanion")
freq_results = (freq_job_radical.run(), freq_job_anion.run())
rkf = freq_results[0].rkfpath(file="adf")
# get_ipython().system('amsspectra {rkf}')
# ## Calculate Vibronic Spectra
# Use Frank-Condon jobs to calculate the vibronic spectra.
def fcf_job(state1, state2, spctype, name):
settings_fcf = Settings()
settings_fcf.input.spectrum.type = spctype
settings_fcf.input.state1 = state1
settings_fcf.input.state2 = state2
return FCFJob(inputjob1=state1, inputjob2=state2, settings=settings_fcf, name=name)
freq_radical = freq_results[0].rkfpath(file="adf")
freq_anion = freq_results[1].rkfpath(file="adf")
fc_abs = fcf_job(freq_radical, freq_anion, "absorption", "fcfabs")
fc_emi = fcf_job(freq_anion, freq_radical, "emission", "fcfemi")
fc_results = (fc_abs.run(), fc_emi.run())
# ## Calculate Density of States
#
# Calculate the DOS by computing the overlap of the absorption and emission FCF spectra.
job = FCFDOS(fc_results[0].kfpath(), fc_results[1].kfpath(), 10000.0, 10000.0)
dos = job.dos()
print(f"The density of states is {dos:.8e}")