1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
|
from typing import List
from hashlib import blake2b
from pydantic import Field
from pymatgen.core.structure import Molecule
from emmet.core.mpid import MPculeID
from emmet.core.material import PropertyOrigin
from emmet.core.qchem.task import TaskDocument
from emmet.core.molecules.molecule_property import PropertyDoc
__author__ = "Evan Spotte-Smith <ewcspottesmith@lbl.gov>"
class VibrationDoc(PropertyDoc):
property_name: str = "vibrations"
molecule: Molecule = Field(..., description="Molecular structure")
frequencies: List[float] = Field(
..., description="List of molecular vibrational frequencies"
)
frequency_modes: List[List[List[float]]] = Field(
..., description="Vibrational frequency modes of the molecule"
)
ir_intensities: List[float] = Field(
...,
title="IR intensities",
description="Intensities for IR vibrational spectrum peaks",
)
ir_activities: List = Field(
...,
title="IR activities",
description="List indicating if frequency-modes are IR-active",
)
@classmethod
def from_task(
cls,
task: TaskDocument,
molecule_id: MPculeID,
deprecated: bool = False,
**kwargs,
): # type: ignore[override]
"""
Construct a vibration document from a task document
:param task: document from which vibrational properties can be extracted
:param molecule_id: MPculeID
:param deprecated: bool. Is this document deprecated?
:param kwargs: to pass to PropertyDoc
:return:
"""
if task.output.frequencies is None:
raise Exception("No frequencies in task!")
if task.output.optimized_molecule is not None:
mol = task.output.optimized_molecule
else:
mol = task.output.initial_molecule
frequencies = task.output.frequencies
frequency_modes = None
intensities = None
active = None
for calc in task.calcs_reversed:
if (
calc.get("frequency_mode_vectors", None) is not None
and frequency_modes is None
):
frequency_modes = calc.get("frequency_mode_vectors")
if calc.get("IR_intens", None) is not None and intensities is None:
intensities = calc.get("IR_intens")
if calc.get("IR_active", None) is not None and active is None:
active = calc.get("IR_active")
if all([x is not None for x in [frequency_modes, intensities, active]]):
break
if frequency_modes is None:
raise Exception("No frequency modes in task!")
elif intensities is None:
raise Exception("No IR intensities in task!")
elif active is None:
raise Exception("No IR activities in task!")
warnings = list()
if frequencies[0] < 0.0:
warnings.append("Imaginary frequencies")
id_string = f"vibrations-{molecule_id}-{task.task_id}-{task.lot_solvent}"
h = blake2b()
h.update(id_string.encode("utf-8"))
property_id = h.hexdigest()
return super().from_molecule(
meta_molecule=mol,
property_id=property_id,
molecule_id=molecule_id,
level_of_theory=task.level_of_theory,
solvent=task.solvent,
lot_solvent=task.lot_solvent,
molecule=mol,
frequencies=frequencies,
frequency_modes=frequency_modes,
ir_intensities=intensities,
ir_activities=active,
warnings=warnings,
origins=[PropertyOrigin(name="vibrations", task_id=task.task_id)],
deprecated=deprecated,
**kwargs,
)
|