Source code for atomate2.aims.run
"""An FHI-aims jobflow runner."""
from __future__ import annotations
import json
import logging
import os
import subprocess
from os.path import expandvars
from typing import TYPE_CHECKING
from ase.calculators.aims import Aims
from ase.calculators.socketio import SocketIOCalculator
from monty.json import MontyDecoder
from pymatgen.io.ase import AseAtomsAdaptor
from atomate2 import SETTINGS
if TYPE_CHECKING:
from pymatgen.core import Molecule, Structure
from atomate2.aims.schemas.task import AimsTaskDoc
logger = logging.getLogger(__name__)
[docs]
def run_aims(
aims_cmd: str = None,
) -> None:
"""
Run FHI-aims.
Parameters
----------
aims_cmd : str
The command used to run FHI-aims (defaults to SETTINGS.AIMS_CMD).
"""
if aims_cmd is None:
aims_cmd = SETTINGS.AIMS_CMD
aims_cmd = expandvars(aims_cmd)
logger.info(f"Running command: {aims_cmd}")
return_code = subprocess.call(["/bin/bash", "-c", aims_cmd], env=os.environ)
logger.info(f"{aims_cmd} finished running with return code: {return_code}")
[docs]
def should_stop_children(
task_document: AimsTaskDoc,
handle_unsuccessful: bool | str = True,
) -> bool:
"""
Decide whether child jobs should continue.
Parameters
----------
task_document : .TaskDoc
An FHI-aims task document.
handle_unsuccessful : bool or str
This is a three-way toggle on what to do if your job looks OK, but is actually
not converged (either electronic or ionic):
- `True`: Mark job as completed, but stop children.
- `False`: Do nothing, continue with workflow as normal.
- `"error"`: Throw an error.
Returns
-------
bool
Whether to stop child jobs.
"""
if task_document.state == "successful":
return False
if isinstance(handle_unsuccessful, bool):
return handle_unsuccessful
if handle_unsuccessful == "error":
raise RuntimeError("Job was not successful (not converged)!")
raise RuntimeError(f"Unknown option for handle_unsuccessful: {handle_unsuccessful}")
[docs]
def run_aims_socket(
structures_to_calculate: list[Structure | Molecule], aims_cmd: str = None
) -> None:
"""Use the ASE interface to run FHI-aims from the socket.
Parameters
----------
structures_to_calculate: list[Structure or Molecule]
The list of structures to run scf calculations on
aims_cmd: str
The aims command to use (defaults to SETTINGS.AIMS_CMD).
"""
if aims_cmd is None:
aims_cmd = SETTINGS.AIMS_CMD
ase_adaptor = AseAtomsAdaptor()
atoms_to_calculate = [
ase_adaptor.get_atoms(structure) for structure in structures_to_calculate
]
with open("parameters.json") as param_file:
parameters = json.load(param_file, cls=MontyDecoder)
del_keys = []
for key, val in parameters.items():
if val is None:
del_keys.append(key)
for key in del_keys:
parameters.pop(key)
if aims_cmd:
parameters["command"] = aims_cmd
elif "command" not in parameters:
parameters["command"] = SETTINGS.AIMS_CMD
calculator = Aims(**parameters)
port = parameters["use_pimd_wrapper"][1]
atoms = atoms_to_calculate[0].copy()
with SocketIOCalculator(calc=calculator, port=port) as calc:
for atoms_calc in atoms_to_calculate:
# Delete prior calculation results
calc.results.clear()
# Reset atoms information to the new cell
atoms.info = atoms_calc.info
atoms.cell = atoms_calc.cell
atoms.positions = atoms_calc.positions
calc.calculate(atoms, system_changes=["positions", "cell"])
calc.close()