"""Reads RESCU+ files.
Read multiple structures and results from ``rescuplus`` output files. Read
structures from ``rescuplus`` input files.
"""
from __future__ import annotations
from pathlib import Path
from typing import TextIO
from ase import Atoms
from ase.calculators.singlepoint import SinglePointDFTCalculator, SinglePointKPoint
from ase.utils.plugins import ExternalIOFormat
from nanotools import TotalEnergy
from nanotools.utils import update_recursive
extin = "rsi"
extout = "rso"
rescuplus_in_format = ExternalIOFormat(
desc="rescuplus infile",
code="1F",
module="nanotools.ext.io.ase",
ext=extin,
)
rescuplus_out_format = ExternalIOFormat(
desc="rescuplus outfile",
code="1F",
module="nanotools.ext.io.ase",
ext=extout,
)
[docs]
def write_rescuplus_in(fid, atoms, **kwargs):
"""
Create an input file for ``rescuplus``.
Units are automatically converted from (angstrom/ev to bohr/hartree).
Args:
fid (file) A file like object to write the input file to.
atoms (Atoms) A single atomistic configuration to write to `fid`.
input_data (dict) A nested dictionary with input parameters for ``rescuplus``.
"""
if "input_data" in kwargs.keys():
input_data = kwargs["input_data"]
else:
input_data = dict()
# update positions and cell shape
atoms_dict = dict(
system=dict(
atoms=dict(
positions=(atoms.positions, "angstrom"),
formula="".join(
atoms.get_chemical_symbols(),
),
),
cell=dict(avec=(atoms.cell.array, "angstrom")),
)
)
input_data = update_recursive(input_data, atoms_dict)
# write file
calc = TotalEnergy(**input_data)
calc.system.atoms.formula = calc.system.atoms.get_formula(format="short")
calc.write(fid, units="atomic")
[docs]
def read_rescuplus_out(fileobj: TextIO | Path) -> Atoms:
"""Reads RESCU+ output files.
The atomistic configurations as well as results (energy, force, stress,
magnetic moments) of the calculation are read for all configurations
within the output JSON file.
Args:
fileobj (file|str): A file object or filename
Returns:
structure (Atoms): The Atoms has a SinglePointCalculator attached with any results
parsed from the file.
"""
if isinstance(fileobj, str):
fileobj = open(fileobj, "rU")
ecalc = TotalEnergy.read(fileobj, units="si")
atoms = ecalc.system.to_ase_atoms()
# Extract calculation results
energy = ecalc.energy.etot.to("eV").m
efermi = ecalc.energy.efermi.to("eV").m
eigenvalues = ecalc.energy.eigenvalues.to("eV").m
forces = None
if ecalc.energy.forces_return:
forces = ecalc.energy.forces.to("eV / angstrom").m
stress = None
if ecalc.energy.stress_return:
stress = -ecalc.energy.stress.to("eV / angstrom ** 3").m
# K-points
ibzkpts = ecalc.system.kpoint.fractional_coordinates
weights = ecalc.system.kpoint.kwght
# magnetic properties
magnetic_moments = None
kpts = []
for s in range(eigenvalues.shape[-1]):
for w, k, e in zip(weights, ibzkpts, eigenvalues[:, :, s]):
kpt = SinglePointKPoint(w, s, k, eps_n=e)
kpts.append(kpt)
# Put everything together
calc = SinglePointDFTCalculator(
atoms,
energy=energy,
forces=forces,
stress=stress,
efermi=efermi,
magmoms=magnetic_moments,
ibzkpts=ibzkpts,
bzkpts=ibzkpts,
)
calc.kpts = kpts
atoms.calc = calc
return atoms, ecalc