ADF Optimization and Vibrational Analysis of Formaldehyde¶
Broadened IR spectrum of optimized formaldehyde.¶
Requires: AMS2026 or later
Related documentation
Calculation¶
Formaldehyde was built from the SMILES string C=O, symmetrized, and optimized as a neutral closed-shell molecule. The optimized structure has C(2V) symmetry. ADF used PBE-D3(BJ), the all-electron DZP basis, and default numerical and SCF settings. Harmonic normal modes and IR intensities were calculated at the final optimized geometry.
Job directory: 01-run_workdir/formaldehyde_adf_pbe_dzp
Provenance: Combined ADF geometry optimization, harmonic frequencies, IR intensities, and PES-point characterization.
Optimized geometry¶
Quantity |
Value |
Unit |
|---|---|---|
C=O |
1.212143 |
angstrom |
C-H(1) |
1.115562 |
angstrom |
C-H(2) |
1.115562 |
angstrom |
mean C-H |
1.115562 |
angstrom |
H-C-H |
115.943895 |
degree |
Minimum verification¶
AMS PES-point classification: local minimum.
Negative harmonic frequencies: 0.
The optimized formaldehyde structure is a local minimum. AMS classified the PES point as local minimum, and all 6 vibrational frequencies are positive.
Vibrational modes¶
The assignments use changes in C=O distance, the two C-H distances, H-C-H angle, and displacement normal to the molecular plane. They are qualitative harmonic-mode descriptions.
Mode |
Frequency (cm^-1) |
IR intensity (km mol^-1) |
Symmetry |
Qualitative assignment |
|---|---|---|---|---|
1 |
1157.06 |
4.682 |
B1 |
CH2 out-of-plane bend |
2 |
1226.38 |
8.786 |
B2 |
CH2 in-plane rocking bend |
3 |
1487.73 |
14.647 |
A1 |
H-C-H scissoring bend |
4 |
1743.28 |
88.378 |
A1 |
C=O stretch |
5 |
2797.14 |
60.003 |
A1 |
symmetric C-H stretch |
6 |
2850.66 |
133.912 |
B2 |
asymmetric C-H stretch |
Broadened IR spectrum¶
The blue curve applies Gaussian broadening with a width of 20 cm^-1 to the calculated sticks. Red lines mark the unbroadened modes. The wavenumber axis follows the usual IR convention.
Normal-mode displacement pictures¶
The maximum atomic displacement in each outer structure is 0.25 angstrom. All views use along_pca3.
Mode 1: 1157.06 cm^-1, B1¶
Assignment: CH2 out-of-plane bend. The left and right structures use equal displacements along the normal coordinate.
Mode 2: 1226.38 cm^-1, B2¶
Assignment: CH2 in-plane rocking bend. The left and right structures use equal displacements along the normal coordinate.
Mode 3: 1487.73 cm^-1, A1¶
Assignment: H-C-H scissoring bend. The left and right structures use equal displacements along the normal coordinate.
Mode 4: 1743.28 cm^-1, A1¶
Assignment: C=O stretch. The left and right structures use equal displacements along the normal coordinate.
Mode 5: 2797.14 cm^-1, A1¶
Assignment: symmetric C-H stretch. The left and right structures use equal displacements along the normal coordinate.
Mode 6: 2850.66 cm^-1, B2¶
Assignment: asymmetric C-H stretch. The left and right structures use equal displacements along the normal coordinate.
AMS input¶
Properties
NormalModes yes
PESPointCharacter yes
End
Task GeometryOptimization
System
Atoms
C 0.0000000000 0.0000000000 0.0337596501
O 0.0000000000 0.0000000000 -1.1856950809
H 0.0000000000 0.9391319492 0.5759677154
H 0.0000000000 -0.9391319492 0.5759677154
End
BondOrders
1 2 2.0
1 3 1.0
1 4 1.0
End
End
Engine adf
Basis
Core None
Type DZP
End
XC
Dispersion GRIMME3 BJDAMP
GGA PBE
End
EndEngine
Conclusion¶
The optimized formaldehyde structure is a local minimum. AMS classified the PES point as local minimum, and all 6 vibrational frequencies are positive. The optimized bond metrics are C=O 1.212143 angstrom, mean C-H 1.115562 angstrom, and H-C-H 115.9439 degrees.
Prompts and Python scripts¶
Prompt (instruction for AI agent)
Use $ams2026
Optimize formaldehyde and calculate its vibrational frequencies with ADF.
Build formaldehyde from SMILES C=O and symmetrize. Run an ADF geometry
optimization with a modest GGA functional, a small-to-medium basis set,
and dispersion.
Verify that the optimized structure is a minimum with no imaginary
frequencies. Report the optimized C=O and C-H bond lengths and the H-C-H angle.
In the report, include a table of vibrational frequencies, IR intensities,
symmetries, and qualitative mode assignments. Plot a simple broadened IR
spectrum from the calculated frequencies and intensities.
For each normal mode, use plot_image_grid along_pca3 to show three pictures
side-by-side where you have displaced-in-negative-direction, optimized
structure, displaced-in-positive-direction.
01-run.py
#!/usr/bin/env amspython
from __future__ import annotations
from pathlib import Path
from scm.base import ChemicalSystem, InputParser
from scm.plams import AMSJob, Settings, finish, init
def build_formaldehyde() -> tuple[ChemicalSystem, str]:
"""Build formaldehyde from the requested SMILES and impose molecular symmetry."""
system = ChemicalSystem.from_smiles("C=O")
point_group = system.symmetrize_molecule(tolerance=0.10)
return system, point_group
def adf_settings() -> Settings:
settings = Settings()
settings.input.ams.Task = "GeometryOptimization"
settings.input.ams.Properties.NormalModes = "Yes"
settings.input.ams.Properties.PESPointCharacter = "Yes"
settings.input.adf.Basis.Type = "DZP"
settings.input.adf.Basis.Core = "None"
settings.input.adf.XC.GGA = "PBE"
settings.input.adf.XC.Dispersion = "GRIMME3 BJDAMP"
return settings
def main() -> None:
system, point_group = build_formaldehyde()
settings = adf_settings()
job = AMSJob(molecule=system, settings=settings, name="formaldehyde_adf_pbe_dzp")
input_text = job.get_input()
InputParser().to_dict("ams", input_text)
Path("validated_input.in").write_text(input_text, encoding="utf-8")
print(f"Built formaldehyde from SMILES C=O and symmetrized it to {point_group}.")
print("Validated the serialized AMS/ADF input against the installed input definitions.")
init(folder="01-run_workdir")
results = job.run()
if not job.ok():
raise RuntimeError(f"AMS job did not finish successfully: {results.job.path}")
print(f"Completed job: {job.path}")
finish()
if __name__ == "__main__":
main()
report.py
#!/usr/bin/env amspython
from __future__ import annotations
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scm.plams import AMSJob, plot_image_grid, view
ROOT = Path(__file__).resolve().parent
TABLES = ROOT / "tables"
FIGURES = ROOT / "figures"
JOB_NAME = "formaldehyde_adf_pbe_dzp"
def latest_job_path() -> Path:
workdirs = [path for path in ROOT.glob("01-run_workdir*") if path.is_dir()]
if not workdirs:
raise FileNotFoundError("No 01-run_workdir directory was found")
latest = max(workdirs, key=lambda path: path.stat().st_mtime)
job_path = latest / JOB_NAME
if not job_path.is_dir():
raise FileNotFoundError(f"Expected AMS job directory: {job_path}")
return job_path
def atom_indices(system) -> tuple[int, int, list[int]]:
carbon = [i for i, atom in enumerate(system.atoms) if atom.symbol == "C"]
oxygen = [i for i, atom in enumerate(system.atoms) if atom.symbol == "O"]
hydrogens = [i for i, atom in enumerate(system.atoms) if atom.symbol == "H"]
if len(carbon) != 1 or len(oxygen) != 1 or len(hydrogens) != 2:
raise ValueError("The optimized system does not have the expected CH2O composition")
return carbon[0], oxygen[0], hydrogens
def symmetry_labels(job: AMSJob, count: int) -> list[str]:
raw = job.results.readrkf("Vibrations", "IrReps", file="engine")
if isinstance(raw, str):
labels = raw.split()
else:
labels = [str(value).strip() for value in np.asarray(raw).reshape(-1)]
if len(labels) != count:
raise ValueError(f"Found {len(labels)} symmetry labels for {count} modes")
return labels
def internal_derivatives(system, mode: np.ndarray) -> dict[str, float]:
c_idx, o_idx, h_idx = atom_indices(system)
step = 0.01
plus = system.copy()
minus = system.copy()
plus.coords[:] = np.asarray(system.coords) + step * mode
minus.coords[:] = np.asarray(system.coords) - step * mode
def central(value_plus: float, value_minus: float) -> float:
return (value_plus - value_minus) / (2.0 * step)
d_co = central(plus.get_distance(c_idx, o_idx), minus.get_distance(c_idx, o_idx))
d_ch = [
central(plus.get_distance(c_idx, h), minus.get_distance(c_idx, h))
for h in h_idx
]
d_angle = central(
plus.get_angle(h_idx[0], c_idx, h_idx[1], unit="degree"),
minus.get_angle(h_idx[0], c_idx, h_idx[1], unit="degree"),
)
coords = np.asarray(system.coords)
molecular_normal = np.cross(coords[o_idx] - coords[c_idx], coords[h_idx[0]] - coords[c_idx])
molecular_normal /= np.linalg.norm(molecular_normal)
oop = sum(abs(np.dot(mode[h] - mode[c_idx], molecular_normal)) for h in h_idx)
return {"co": d_co, "ch1": d_ch[0], "ch2": d_ch[1], "angle": d_angle, "oop": oop}
def assign_modes(frequencies: np.ndarray, modes: np.ndarray, system) -> list[str]:
metrics = [internal_derivatives(system, mode) for mode in modes]
assignments = [""] * len(frequencies)
high = [i for i, frequency in enumerate(frequencies) if frequency > 2200.0]
low = [i for i in range(len(frequencies)) if i not in high]
for i in high:
same_phase = metrics[i]["ch1"] * metrics[i]["ch2"] >= 0.0
assignments[i] = "symmetric C-H stretch" if same_phase else "asymmetric C-H stretch"
if low:
oop_idx = max(low, key=lambda i: metrics[i]["oop"])
assignments[oop_idx] = "CH2 out-of-plane bend"
remaining = [i for i in low if i != oop_idx]
if remaining:
co_idx = max(remaining, key=lambda i: abs(metrics[i]["co"]))
assignments[co_idx] = "C=O stretch"
remaining.remove(co_idx)
if remaining:
scissor_idx = max(remaining, key=lambda i: abs(metrics[i]["angle"]))
assignments[scissor_idx] = "H-C-H scissoring bend"
remaining.remove(scissor_idx)
for i in remaining:
assignments[i] = "CH2 in-plane rocking bend"
return [assignment or "mixed vibration" for assignment in assignments]
def make_mode_grids(system, modes: np.ndarray) -> list[Path]:
grid_paths: list[Path] = []
optimized_image = FIGURES / "optimized_structure.png"
view(
system,
guess_bonds=len(system.bonds) == 0,
direction="along_pca3",
width=500,
height=400,
picture_path=str(optimized_image),
)
for number, mode in enumerate(modes, start=1):
max_norm = float(np.linalg.norm(mode, axis=1).max())
scale = 0.25 / max_norm
negative = system.copy()
positive = system.copy()
negative.coords[:] = np.asarray(system.coords) - scale * mode
positive.coords[:] = np.asarray(system.coords) + scale * mode
images = {}
for label, structure in [
("negative displacement", negative),
("optimized structure", system),
("positive displacement", positive),
]:
image_path = FIGURES / f"mode_{number:02d}_{label.replace(' ', '_')}.png"
images[label] = view(
structure,
guess_bonds=len(structure.bonds) == 0,
direction="along_pca3",
width=360,
height=300,
picture_path=str(image_path),
)
grid_path = FIGURES / f"mode_{number:02d}_grid.png"
plot_image_grid(images, rows=1, figsize=(12, 3.5), save_path=str(grid_path))
plt.close("all")
grid_paths.append(grid_path)
return grid_paths
def markdown_image(path: Path, alt: str) -> str:
return f".as_posix()})"
def format_input(input_text: str) -> str:
return f"```ams\n{input_text.rstrip()}\n```"
def main() -> None:
TABLES.mkdir(exist_ok=True)
FIGURES.mkdir(exist_ok=True)
job_path = latest_job_path()
job = AMSJob.load_external(str(job_path))
provenance = "Combined ADF geometry optimization, harmonic frequencies, IR intensities, and PES-point characterization."
system = job.results.get_main_system()
symmetry_probe = system.copy()
optimized_point_group = symmetry_probe.symmetrize_molecule(tolerance=0.05)
frequencies = np.asarray(job.results.get_frequencies(unit="cm^-1"), dtype=float)
intensities = np.asarray(job.results.get_ir_intensities(), dtype=float)
modes = np.asarray(job.results.get_normal_modes(), dtype=float)
labels = symmetry_labels(job, len(frequencies))
assignments = assign_modes(frequencies, modes, system)
c_idx, o_idx, h_idx = atom_indices(system)
co_length = system.get_distance(c_idx, o_idx)
ch_lengths = [system.get_distance(c_idx, h) for h in h_idx]
hch_angle = system.get_angle(h_idx[0], c_idx, h_idx[1], unit="degree")
try:
pes_character = str(job.results.readrkf("AMSResults", "PESPointCharacter", file="engine")).strip()
except KeyError:
pes_character = str(job.results.readrkf("AMSResults", "PESPointCharacter", file="ams")).strip()
imaginary = frequencies[frequencies < 0.0]
is_minimum = len(imaginary) == 0 and "minimum" in pes_character.lower()
frequency_table = pd.DataFrame(
{
"Mode": np.arange(1, len(frequencies) + 1),
"Frequency (cm^-1)": np.round(frequencies, 2),
"IR intensity (km mol^-1)": np.round(intensities, 3),
"Symmetry": labels,
"Qualitative assignment": assignments,
}
)
frequency_table.to_csv(TABLES / "vibrational_modes.csv", index=False)
geometry_table = pd.DataFrame(
{
"Quantity": ["C=O", "C-H(1)", "C-H(2)", "mean C-H", "H-C-H"],
"Value": [co_length, ch_lengths[0], ch_lengths[1], np.mean(ch_lengths), hch_angle],
"Unit": ["angstrom", "angstrom", "angstrom", "angstrom", "degree"],
}
)
geometry_table.to_csv(TABLES / "optimized_geometry.csv", index=False)
spectrum_x, spectrum_y = job.results.get_ir_spectrum(
broadening_type="gaussian",
broadening_width=20,
min_x=400,
max_x=3400,
x_spacing=1.0,
)
fig, ax = plt.subplots(figsize=(8, 4.5))
ax.plot(spectrum_x, spectrum_y, color="#214f8b", linewidth=1.6)
ax.vlines(frequencies, 0.0, intensities, color="#a33a2b", linewidth=0.8, alpha=0.7)
ax.set_xlim(3400, 400)
ax.set_xlabel(r"Wavenumber (cm$^{-1}$)")
ax.set_ylabel(r"IR intensity (km mol$^{-1}$)")
ax.set_title("Harmonic IR spectrum, Gaussian width 20 cm$^{-1}$")
fig.tight_layout()
spectrum_path = FIGURES / "ir_spectrum.png"
fig.savefig(spectrum_path, dpi=180)
plt.close(fig)
mode_grid_paths = make_mode_grids(system, modes)
mode_sections = []
for row, grid_path in zip(frequency_table.itertuples(index=False), mode_grid_paths):
mode_sections.append(
f"### Mode {row.Mode}: {row._1:.2f} cm^-1, {row.Symmetry}\n\n"
f"Assignment: {row._4}. The left and right structures use equal displacements along the normal coordinate.\n\n"
f"{markdown_image(grid_path, f'Mode {row.Mode} displacement grid')}"
)
if is_minimum:
conclusion = (
"The optimized formaldehyde structure is a local minimum. AMS classified the PES point as "
f"{pes_character}, and all {len(frequencies)} vibrational frequencies are positive."
)
else:
conclusion = (
"The minimum test did not pass. The PES classification is "
f"{pes_character}, and the number of negative frequencies is {len(imaginary)}."
)
report = f"""# ADF optimization and vibrational analysis of formaldehyde
## Calculation
Formaldehyde was built from the SMILES string `C=O`, symmetrized, and optimized as a neutral closed-shell molecule. The optimized structure has {optimized_point_group} symmetry. ADF used PBE-D3(BJ), the all-electron DZP basis, and default numerical and SCF settings. Harmonic normal modes and IR intensities were calculated at the final optimized geometry.
Job directory: `{job_path.relative_to(ROOT)}`
Provenance: {provenance}
## Optimized geometry
{geometry_table.to_markdown(index=False, floatfmt='.6f')}
{markdown_image(FIGURES / 'optimized_structure.png', 'Optimized formaldehyde structure')}
## Minimum verification
AMS PES-point classification: `{pes_character}`.
Negative harmonic frequencies: {len(imaginary)}.
{conclusion}
## Vibrational modes
The assignments use changes in C=O distance, the two C-H distances, H-C-H angle, and displacement normal to the molecular plane. They are qualitative harmonic-mode descriptions.
{frequency_table.to_markdown(index=False)}
## Broadened IR spectrum
The blue curve applies Gaussian broadening with a width of 20 cm^-1 to the calculated sticks. Red lines mark the unbroadened modes. The wavenumber axis follows the usual IR convention.
{markdown_image(spectrum_path, 'Broadened harmonic IR spectrum')}
## Normal-mode displacement pictures
The maximum atomic displacement in each outer structure is 0.25 angstrom. All views use `along_pca3`.
{chr(10).join(mode_sections)}
## AMS input
{format_input(job.get_input())}
## Conclusion
{conclusion} The optimized bond metrics are C=O {co_length:.6f} angstrom, mean C-H {np.mean(ch_lengths):.6f} angstrom, and H-C-H {hch_angle:.4f} degrees.
"""
(ROOT / "report.md").write_text(report, encoding="utf-8")
print(f"Wrote {ROOT / 'report.md'}")
print(conclusion)
if __name__ == "__main__":
main()
Original Markdown report
# ADF optimization and vibrational analysis of formaldehyde
## Calculation
Formaldehyde was built from the SMILES string `C=O`, symmetrized, and optimized as a neutral closed-shell molecule. The optimized structure has C(2V) symmetry. ADF used PBE-D3(BJ), the all-electron DZP basis, and default numerical and SCF settings. Harmonic normal modes and IR intensities were calculated at the final optimized geometry.
Job directory: `01-run_workdir/formaldehyde_adf_pbe_dzp`
Provenance: Combined ADF geometry optimization, harmonic frequencies, IR intensities, and PES-point characterization.
## Optimized geometry
| Quantity | Value | Unit |
|:-----------|-----------:|:---------|
| C=O | 1.212143 | angstrom |
| C-H(1) | 1.115562 | angstrom |
| C-H(2) | 1.115562 | angstrom |
| mean C-H | 1.115562 | angstrom |
| H-C-H | 115.943895 | degree |

## Minimum verification
AMS PES-point classification: `local minimum`.
Negative harmonic frequencies: 0.
The optimized formaldehyde structure is a local minimum. AMS classified the PES point as local minimum, and all 6 vibrational frequencies are positive.
## Vibrational modes
The assignments use changes in C=O distance, the two C-H distances, H-C-H angle, and displacement normal to the molecular plane. They are qualitative harmonic-mode descriptions.
| Mode | Frequency (cm^-1) | IR intensity (km mol^-1) | Symmetry | Qualitative assignment |
|-------:|--------------------:|---------------------------:|:-----------|:--------------------------|
| 1 | 1157.06 | 4.682 | B1 | CH2 out-of-plane bend |
| 2 | 1226.38 | 8.786 | B2 | CH2 in-plane rocking bend |
| 3 | 1487.73 | 14.647 | A1 | H-C-H scissoring bend |
| 4 | 1743.28 | 88.378 | A1 | C=O stretch |
| 5 | 2797.14 | 60.003 | A1 | symmetric C-H stretch |
| 6 | 2850.66 | 133.912 | B2 | asymmetric C-H stretch |
## Broadened IR spectrum
The blue curve applies Gaussian broadening with a width of 20 cm^-1 to the calculated sticks. Red lines mark the unbroadened modes. The wavenumber axis follows the usual IR convention.

## Normal-mode displacement pictures
The maximum atomic displacement in each outer structure is 0.25 angstrom. All views use `along_pca3`.
### Mode 1: 1157.06 cm^-1, B1
Assignment: CH2 out-of-plane bend. The left and right structures use equal displacements along the normal coordinate.

### Mode 2: 1226.38 cm^-1, B2
Assignment: CH2 in-plane rocking bend. The left and right structures use equal displacements along the normal coordinate.

### Mode 3: 1487.73 cm^-1, A1
Assignment: H-C-H scissoring bend. The left and right structures use equal displacements along the normal coordinate.

### Mode 4: 1743.28 cm^-1, A1
Assignment: C=O stretch. The left and right structures use equal displacements along the normal coordinate.

### Mode 5: 2797.14 cm^-1, A1
Assignment: symmetric C-H stretch. The left and right structures use equal displacements along the normal coordinate.

### Mode 6: 2850.66 cm^-1, B2
Assignment: asymmetric C-H stretch. The left and right structures use equal displacements along the normal coordinate.

## AMS input
```ams
Properties
NormalModes yes
PESPointCharacter yes
End
Task GeometryOptimization
System
Atoms
C 0.0000000000 0.0000000000 0.0337596501
O 0.0000000000 0.0000000000 -1.1856950809
H 0.0000000000 0.9391319492 0.5759677154
H 0.0000000000 -0.9391319492 0.5759677154
End
BondOrders
1 2 2.0
1 3 1.0
1 4 1.0
End
End
Engine adf
Basis
Core None
Type DZP
End
XC
Dispersion GRIMME3 BJDAMP
GGA PBE
End
EndEngine
```
## Conclusion
The optimized formaldehyde structure is a local minimum. AMS classified the PES point as local minimum, and all 6 vibrational frequencies are positive. The optimized bond metrics are C=O 1.212143 angstrom, mean C-H 1.115562 angstrom, and H-C-H 115.9439 degrees.