ωB97M(2) Calculation

This tutorial demonstrates the use of ORCA together with the ORCA python interface (OPI) to perform a calculation with the ωB97M(2) double hybrid functional (J. Chem. Phys. 148, 241736 (2018)). First, a single-point DFT calculation is performed with ωB97M-V and the resulting orbitals are then used to run a MP2 calculation. The final energies are combined to obtain the ωB97M(2) energy.

from pathlib import Path
from typing import Any

from opi.core import Calculator
from opi.output.core import Output
from opi.input.structures.structure import Structure
from opi.input.simple_keywords import BasisSet, AuxBasisSet, DispersionCorrection, Dft, Grid, Scf
from opi.input.blocks import BlockBasis, BlockMp2, BlockScf
def check_and_parse_output(output: Output)-> None:
    # > Check for proper termination of ORCA
    status = output.terminated_normally()
    if not status:
        # > ORCA did not terminate normally
        raise RuntimeError(f"ORCA did not terminate normally, see output file: {output.get_outfile()}")
    else:
        # > ORCA did terminate normally so we can parse the output
        output.parse()

    # Now check for convergence of the SCF
    if not output.results_properties.geometries[0].single_point_data.converged:
        raise RuntimeError("SCF DID NOT CONVERGE")
def run_wb97m2(structure: Structure, basename : str = "job", working_dir: Path = Path("RUN"), charge = 0, mult = 1) -> dict[str, Any]:
    # > Task 1: ωB97M-V/def2-TZVP
    # > Create a Calculator object for ORCA input generation and execution
    calc = Calculator(basename=f"{basename}_scf", working_dir=working_dir)

    calc.structure = structure
    calc.structure.charge = charge
    calc.structure.multiplicity = mult

    # > Add calculation keywords and blocks
    calc.input.add_simple_keywords(
        Dft.WB97M_V,
        DispersionCorrection.SCNL,
        Grid.DEFGRID2,
    )
    calc.input.add_blocks(
        BlockBasis(basis=BasisSet.DEF2_TZVP, auxj=AuxBasisSet.DEF2_J)
    )

    # > Write the ORCA input file
    calc.write_input()
    # > Run the ORCA calculation
    calc.run()
    # > Get the output object
    output_scf = calc.get_output()
    check_and_parse_output(output_scf)

    # > Task 2: ωB97M(2)/def2-TZVP
    calc = Calculator(basename=f"{basename}_mp2", working_dir=working_dir)
    calc.structure = structure
    calc.structure.charge = charge
    calc.structure.multiplicity = mult
    calc.input.add_simple_keywords(
        Dft.WB97M_2,
        DispersionCorrection.SCNL,
        Grid.DEFGRID2,
        Scf.NOITER,
        Scf.CALCGUESSENERGY,
        Scf.MOREAD
    )
    calc.input.add_blocks(
        BlockBasis(basis=BasisSet.DEF2_TZVP, auxc=AuxBasisSet.DEF2_TZVP_C, auxj=AuxBasisSet.DEF2_J),
        BlockScf(ignoreconv=1),
        BlockMp2(ri=True)
    )
    calc.input.moinp = working_dir / f"{basename}_scf.gbw"
    calc.write_input()
    calc.run()
    output_mp2 = calc.get_output()
    check_and_parse_output(output_mp2)

    E_Total_wB97MV = output_scf.results_properties.geometries[0].energy[0].totalenergy[0][0]
    E_Total_wB97M2 = output_mp2.results_properties.geometries[0].energy[1].totalenergy[0][0]

    result = {
        "ωB97M-V/def2-TZVP": {
            "totalEnergy": float(f"{E_Total_wB97MV:.10f}")
        },
        "ωB97M(2)/def2-TZVP": {
            "totalEnergy": float(f"{E_Total_wB97M2:.10f}")
        }
    }

    return result
if __name__ == "__main__":
    # > Create a working directory and XYZ file
    # > You can replace everything up to the next comment with your own directory or file
    working_dir = Path("wb97m2")
    working_dir.mkdir(exist_ok=True)

    xyz_data = """\
    3

    O         -3.56626        1.77639        0.00000
    H         -2.59626        1.77639        0.00000
    H         -3.88959        1.36040       -0.81444
    """
    xyz_file = working_dir / "struc.xyz"
    with open(xyz_file, "w") as f:
        f.write(xyz_data)

    # > Load the molecular structure from XYZ file
    structure = Structure.from_xyz(xyz_file)

    # > Define a basename for the calculation files
    basename = "wb97m2"

    # > Run wB97M-V and wB97M(2) calculations
    result = run_wb97m2(structure, basename, working_dir)

    # > Print the results
    for method, energies in result.items():
        print(f"\nMethod: {method}")
        for key, value in energies.items():
            print(f"  {key}: {value}")
Method: ωB97M-V/def2-TZVP
  totalEnergy: -76.4290431087

Method: ωB97M(2)/def2-TZVP
  totalEnergy: -76.4672818124