Exercises#
In this exercise we will look at how substitution affects the vibrational frequencies, IR, and Raman spectra.
# import section
import numpy as np
import py3Dmol as p3d
import veloxchem as vlx
from matplotlib import pyplot as plt
basis_set_label = "6-31G"
# define molecules and basis sets
ethene_xyz = """6
ethene
C 0.000000 -0.663984 0.000000
C 0.000000 0.663984 0.000000
H 0.919796 -1.223061 0.000000
H -0.919796 -1.223061 0.000000
H 0.919796 1.223061 0.000000
H -0.919796 1.223061 0.000000
"""
ethene = vlx.Molecule.read_xyz_string(ethene_xyz)
ethene_basis = vlx.MolecularBasis.read(ethene, basis_set_label, ostream=None)
fluoroethene_xyz = """6
fluoroethene
C 0.000000 -0.663984 0.000000
C 0.000000 0.663984 0.000000
F 1.519796 -1.223061 0.000000
H -0.919796 -1.223061 0.000000
H 0.919796 1.223061 0.000000
H -0.919796 1.223061 0.000000
"""
fluoroethene = vlx.Molecule.read_xyz_string(fluoroethene_xyz)
fluoroethene_basis = vlx.MolecularBasis.read(fluoroethene, basis_set_label, ostream=None)
chloroethene_xyz = """6
chloroethene
C 0.000000 -0.663984 0.000000
C 0.000000 0.663984 0.000000
Cl 1.519796 -1.223061 0.000000
H -0.919796 -1.223061 0.000000
H 0.919796 1.223061 0.000000
H -0.919796 1.223061 0.000000
"""
chloroethene = vlx.Molecule.read_xyz_string(chloroethene_xyz)
chloroethene_basis = vlx.MolecularBasis.read(chloroethene, basis_set_label, ostream=None)
view = p3d.view(linked=True, viewergrid=(1, 3), width=600, height=200)
view.addModel(ethene_xyz, "xyz", viewer=(0, 0))
view.addModel(fluoroethene_xyz, "xyz", viewer=(0, 1))
view.addModel(chloroethene_xyz, "xyz", viewer=(0, 2))
view.setViewStyle({"style": "outline", "width": 0.05})
view.setStyle({"stick": {}, "sphere": {"scale": 0.25}})
view.zoomTo()
view.show()
3Dmol.js failed to load for some reason. Please check your browser console for error messages.
SCF geometry optimization#
Before we can calculate the vibrational spectra, we first must optimize the geometries.
# Settings for SCF and gradient drivers
scf_settings = {"conv_thresh": 1e-6}
method_settings = {}
# Run SCF for ethene
ethene_scf_drv = ...
...
# Run SCF for fluoroethene
fluoroethene_scf_drv = ...
...
# Run SCF for chloroethene
chloroethene_scf_drv = ...
...
# Set up the gradient and optimization dirvers:
ethene_grad_drv = ...
fluoroethene_grad_drv = ...
...
chloroethene_grad_drv = ...
...
# Optimize the geometries
opt_ethene = ...
opt_fluoroethene = ...
opt_chloroethene = ...
# Get optimized coordinates as xyz string
def get_xyz(molecule):
natm = molecule.number_of_atoms()
elements = molecule.get_labels()
coords = molecule.get_coordinates_in_angstrom()
txt = "%d\n\n" % natm
for i in range(natm):
txt += elements[i] + " %15.7f %15.7f %15.7f\n" % (coords[i,0], coords[i,1], coords[i,2])
return txt
# Visualize the optimized structures
...
# Compute SCF with the optimized geometries of all molecules
...
Hessians#
Now, we can calculate the IR and Raman spectra for these optimized geometries.
# Settings for Hessian calculation
hessian_settings = {"do_raman": "yes", "print_depolarization_ratio": "no"}
# Create Hessian driver and update settings
ethene_vibanalysis_drv = ...
...
fluoroethene_vibanalysis_drv = ...
...
chloroethene_vibanalysis_drv = ...
...
# Compute the Hessians:
...
...
...
# Broadening function
def add_broadening(list_ex_energy, list_osci_strength, line_profile='Lorentzian', line_param=10, step=10):
...
return x, y
# To animate the normal mode we will need both the geometry and the displacements
def get_normal_mode(molecule, normal_mode):
...
return vib_xyz
Vibrational analysis#
To get a summary of the vibrational analysis, one simply needs to run the following command:
# Run for all molecules
ethene_vibanalysis_drv.vibrational_analysis(opt_ethene)
...
...
Think about the dipole moment of these molecules and consider the Hydrogen stretching modes. How do you expect the IR spectra to look like? Which molecule do you expect will have more intense IR-peaks?
# Plot the IR spectra
plt.figure(figsize=(6, 4))
eth_x, eth_ir = ethene_vibanalysis_drv.vib_frequencies, ethene_vibanalysis_drv.ir_intensities
flo_x, flo_ir = (
fluoroethene_vibanalysis_drv.vib_frequencies,
fluoroethene_vibanalysis_drv.ir_intensities,
)
chl_x, chl_ir = (
chloroethene_vibanalysis_drv.vib_frequencies,
chloroethene_vibanalysis_drv.ir_intensities,
)
eth_xl, eth_irl = add_broadening(
eth_x, eth_ir, line_profile="Lorentzian", line_param=20, step=2
)
flo_xl, flo_irl = add_broadening(
flo_x, flo_ir, line_profile="Lorentzian", line_param=20, step=2
)
chl_xl, chl_irl = add_broadening(
chl_x, chl_ir, line_profile="Lorentzian", line_param=20, step=2
)
plt.plot(eth_xl, eth_irl, label="Ethene")
plt.plot(flo_xl, flo_irl, label="Fluoroethene")
plt.plot(chl_xl, chl_irl, label="Chloroethene")
plt.xlabel("Wavenumber (cm**-1)")
plt.axis(xmin=3200, xmax=3500)
plt.axis(ymin=-0.2, ymax=1.5)
plt.ylabel("IR intensity (km/mol)")
plt.title("Calculated IR sepctra, H-stretching region")
plt.legend()
plt.tight_layout()
plt.show()

# Plot the Raman spectra
plt.figure(figsize=(6, 4))
eth_x, eth_raman = ethene_vibanalysis_drv.vib_frequencies, ethene_vibanalysis_drv.raman_activities
flo_x, flo_raman = (
fluoroethene_vibanalysis_drv.vib_frequencies,
fluoroethene_vibanalysis_drv.raman_activities,
)
chl_x, chl_raman = (
chloroethene_vibanalysis_drv.vib_frequencies,
chloroethene_vibanalysis_drv.raman_activities,
)
freq = 0
eth_xl, eth_ramanl = add_broadening(
eth_x, eth_raman[freq], line_profile="Lorentzian", line_param=20, step=2
)
flo_xl, flo_ramanl = add_broadening(
flo_x, flo_raman[freq], line_profile="Lorentzian", line_param=20, step=2
)
chl_xl, chl_ramanl = add_broadening(
chl_x, chl_raman[freq], line_profile="Lorentzian", line_param=20, step=2
)
plt.plot(eth_xl, eth_ramanl, label="Ethene")
plt.plot(flo_xl, flo_ramanl, label="Fluoroethene")
plt.plot(chl_xl, chl_ramanl, label="Chloroethene")
plt.xlabel("Wavenumber (cm**-1)")
plt.axis(xmin=3200, xmax=3500)
plt.axis(ymin=-0.2, ymax=7)
plt.ylabel("Raman activity (A**4/amu)")
plt.title("Calculated Raman sepctra, H-stretching region")
plt.legend()
plt.tight_layout()
plt.show()

# Get the displacements of the normal mode
ethene_h1 = get_normal_mode(ethene, ethene_vibanalysis_drv.normal_modes[-1])
fluoroethene_h1 = get_normal_mode(
fluoroethene, fluoroethene_vibanalysis_drv.normal_modes[-1]
)
chloroethene_h1 = get_normal_mode(
chloroethene, chloroethene_vibanalysis_drv.normal_modes[-1]
)
# Animate the vibration
view = p3d.view(viewergrid=(1, 3), width=600, height=200, linked=True)
view.addModel(
ethene_h1, "xyz", {"vibrate": {"frames": 10, "amplitude": 0.75}}, viewer=(0, 0)
)
view.addModel(
fluoroethene_h1,
"xyz",
{"vibrate": {"frames": 10, "amplitude": 0.75}},
viewer=(0, 1),
)
view.addModel(
chloroethene_h1,
"xyz",
{"vibrate": {"frames": 10, "amplitude": 0.75}},
viewer=(0, 2),
)
view.setViewStyle({"style": "outline", "width": 0.05})
view.setStyle({"stick": {}, "sphere": {"scale": 0.25}})
view.animate({"loop": "backAndForth"})
view.zoomTo()
view.show()
3Dmol.js failed to load for some reason. Please check your browser console for error messages.
IR intensities and Raman activities#
To rationalize why the IR and Raman spectra look as they do, calculate how the dipole moment and polarizability change during particular vibrational motions. Look at the Hydrogen stretching modes and select a mode which is IR-active in ethene, but is suppressed in fluoroethene or chloroethene. What is the dipole moment in the optimized molecule? How does the dipole moment change during the vibration?
# Calculate the dipole moment of the optimized molecules
# For this we will use the FirstOrderProperties class from veloxchem
ethene_prop = vlx.firstorderprop.FirstOrderProperties()
ethene_prop.compute_scf_prop(opt_ethene, ethene_basis, ethene_scf_results)
ethene_dipole_moment = ethene_prop.get_property('dipole moment')
...
# Select normal mode and get the array of atomic displacements
...
# Use the atomic displacement array to construct several new molecular configurations
# along the vibrational mode, e.g. -0.75, -0.5, -0.25, 0.25, 0.5, 0.75 displacement
...
# Calculate the dipole moment for the new configurations
...
# Plot as a function of displacement
...
Now select a mode which is Raman-active in ethene, but is suppressed in fluoroethene or chloroethene. Calculate the polarizability of the optimized molecule. How does the polarizability change during the vibration?
# Calculate the polarizability of the optimized molecules
# For this, we need to run a linear response calculation
ethene_lrdrv = vlx.lrsolver.LinearResponseSolver()
ethene_pol_dict = ethene_lrdrv.compute(opt_ethene, ethene_basis, ethene_scf_results)
ethene_polarizability = ethene_pol_dict['response_functions']
...
# Select normal mode and get the array of atomic displacements
...
# Use the atomic displacement array to construct several new molecular configurations
# along the vibrational mode, e.g. -0.75, -0.5, -0.25, 0.25, 0.5, 0.75 displacement
...
# Calculate the polarizability for the new configurations
...
# Plot component or norm as a function of displacement
...