#!/usr/bin/env amspython
# coding: utf-8
# ## Purpose
# Example showing the `UseLowestEnergy` feature of the Hybrid engine and the `OptimizeSpinRound` feature of the ADF engine.
#
# A bond scan is performed on hydrogen peroxide with UFF. The resulting
# structures are then recalculated with DFT using four different setups. In
# particular, one setup uses the Hybrid engine with DynamicFactors=UseLowestEnergy.
# Another uses the ADF engine with `OptimizeSpinRound` in combination with an `ElectronicTemperature`.
#
# In the end, the results of energy vs. bond length are plotted.
#
# ## Initial imports
import matplotlib.pyplot as plt
from scm.plams import from_smiles, Settings, AMSJob, Units, init
# this line is not required in AMS2025+
init()
# ## Helper functions
def initial_pesscan():
"""Run a bond scan for hydrogen peroxide with UFF. Returns the finished job"""
mol = from_smiles("OO") # hydrogen peroxide
s = Settings()
s.input.ams.Task = "PESScan"
s.input.ams.PESScan.ScanCoordinate.nPoints = 7
# Scan O-O bond length (atoms 1 and 2) between 1.2 and 2.5 Å
s.input.ams.PESScan.ScanCoordinate.Distance = "2 1 1.2 2.5"
s.input.ForceField.Type = "UFF"
job = AMSJob(settings=s, molecule=mol, name="initial_pesscan")
job.run()
return job
def singlet_settings(header=""):
s = Settings()
s.Unrestricted = "No"
s.XC.GGA = "PBE"
s.Basis.Type = "DZP"
s.SCF.Iterations = 100
s._h = header
return s
def triplet_settings(header=""):
s = Settings()
s.Unrestricted = "Yes"
s.SpinPolarization = 2
s.XC.GGA = "PBE"
s.Basis.Type = "DZP"
s.SCF.Iterations = 100
s._h = header
return s
def hybrid_settings():
"""Look at the file plams_workdir/hybrid/hybrid.in to see how the below is translated into text input for the hybrid engine"""
s = Settings()
s.input.Hybrid.Energy.DynamicFactors = "UseLowestEnergy"
s.input.Hybrid.Energy.Term = ["Region=* EngineID=Singlet", "Region=* EngineID=Triplet"]
s.input.Hybrid.Engine = [singlet_settings("ADF Singlet"), triplet_settings("ADF Triplet")]
return s
def replay_job(rkf, engine="hybrid"):
"""Replay the structures from the UFF pesscan with ADF. Use three different engines:
Hybrid: This will run all structures with both Singlet and Triplet and pick the lowest energy
Singlet: This runs all structures using the Singlet settings
Triplet: This runs all structures using the Triplet settings
Returns the finished job
"""
s = Settings()
if engine == "hybrid":
s = hybrid_settings()
elif engine == "singlet":
s.input.adf = singlet_settings()
elif engine == "triplet":
s.input.adf = triplet_settings()
elif engine == "optimizespin":
s.input.adf = triplet_settings()
s.input.adf.Occupations = "ElectronicTemperature=300 OptimizeSpinRound=0.05"
s.input.ams.Task = "Replay"
s.input.ams.Replay.File = rkf
job = AMSJob(settings=s, name=engine)
job.run()
return job
def plot_results(singlet_job, triplet_job, hybrid_job, optimizespin_job):
"""
Generate a plot of the energy vs. bond length for the three different jobs. Saves a plot to pesplot.png.
"""
bondlengths = singlet_job.results.get_pesscan_results()["RaveledPESCoords"][0]
bondlengths = Units.convert(bondlengths, "bohr", "angstrom")
singlet_pes = singlet_job.results.get_pesscan_results()["PES"]
triplet_pes = triplet_job.results.get_pesscan_results()["PES"]
hybrid_pes = hybrid_job.results.get_pesscan_results()["PES"]
hybrid_pes = [x - 0.005 for x in hybrid_pes] # slightly downshift for visual clarity when plotting
optimizespin_pes = optimizespin_job.results.get_pesscan_results()["PES"]
optimizespin_pes = [x - 0.010 for x in optimizespin_pes] # slightly downshift for visual clarity when plotting
plt.plot(bondlengths, singlet_pes)
plt.plot(bondlengths, triplet_pes)
plt.plot(bondlengths, hybrid_pes)
plt.plot(bondlengths, optimizespin_pes)
plt.title("PES Scan for hydrogen peroxide")
plt.xlabel("O-O bond length (Å)")
plt.ylabel("Energy (hartree)")
plt.legend(
["Singlet", "Triplet", "Hybrid: Lowest energy (slightly shifted)", "ADF: optimized spin (slightly shifted)"]
)
plt.savefig("pesplot.png")
plt.show()
# ## Run the job
pesscan_job = initial_pesscan()
rkf = pesscan_job.results.rkfpath()
singlet_job = replay_job(rkf, "singlet")
triplet_job = replay_job(rkf, "triplet")
hybrid_job = replay_job(rkf, "hybrid")
optimizespin_job = replay_job(rkf, "optimizespin")
# or load the finished jobs from disk:
# pesscan_job = AMSJob.load_external('plams_workdir.002/initial_pesscan/ams.rkf')
# rkf = pesscan_job.results.rkfpath()
# singlet_job = AMSJob.load_external('plams_workdir.002/singlet/ams.rkf')
# triplet_job = AMSJob.load_external('plams_workdir.002/triplet/ams.rkf')
# hybrid_job = AMSJob.load_external('plams_workdir.002/hybrid/ams.rkf')
# optimizespin_job = AMSJob.load_external('plams_workdir.002/optimizespin/ams.rkf')
# ## Plot the result
#
# Two equivalent to follow the lowest energy of the singlet and the triplet state, with either the Hybrid engine (green) or the optimize spin option (red) from ADF.
plot_results(singlet_job, triplet_job, hybrid_job, optimizespin_job)