# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
from __future__ import annotations
"""
Sile object for reading/writing FHI-aims geometry files
"""
import numpy as np
from sisl import Geometry, Lattice
from sisl._internal import set_module
from sisl.messages import deprecate_argument
from ..sile import SileError, add_sile, sile_fh_open, sile_raise_write
from .sile import SileFHIaims
__all__ = ["inSileFHIaims"]
@set_module("sisl.io.fhiaims")
class inSileFHIaims(SileFHIaims):
"""FHI-aims ``geometry.in`` file object"""
[docs]
@sile_fh_open()
@deprecate_argument("sc", "lattice", "use lattice= instead of sc=", "0.15", "0.16")
def write_lattice(self, lattice: Lattice, fmt: str = ".8f"):
"""Writes the supercell to the contained file
Parameters
----------
lattice :
the supercell to be written
fmt :
used format for the precision of the data
"""
sile_raise_write(self)
_fmt = f"lattice_vector {{:{fmt}}} {{:{fmt}}} {{:{fmt}}}\n"
self._write(_fmt.format(*lattice.cell[0]))
self._write(_fmt.format(*lattice.cell[1]))
self._write(_fmt.format(*lattice.cell[2]))
[docs]
@sile_fh_open()
def write_geometry(
self,
geometry: Geometry,
fmt: str = ".8f",
as_frac: bool = False,
velocity=None,
moment=None,
):
"""Writes the geometry to the contained file
Parameters
----------
geometry :
the geometry to be written
fmt :
used format for the precision of the data
as_frac :
whether coordinates are written as fractional coordinates
velocity: array_like, optional
also write the velocity fields in [Ang/ps]
moment : array_like, optional
also write an initial moment for each atom
"""
# Check that we can write to the file
sile_raise_write(self)
self.write_lattice(geometry.lattice, fmt)
if as_frac:
xyz = geometry.fxyz
prefix = "atom_frac"
else:
xyz = geometry.xyz
prefix = "atom"
_fmt = f"{prefix} {{1:{fmt}}} {{2:{fmt}}} {{3:{fmt}}} {{0:s}}\n"
_fmtv = f"velocity {{:{fmt}}} {{:{fmt}}} {{:{fmt}}}\n"
_fmtm = f"initial_moment {{:{fmt}}}\n"
for ia, atom in enumerate(geometry.atoms):
s = {"fa": "Ds"}.get(atom.symbol, atom.symbol)
self._write(_fmt.format(s, *xyz[ia]))
if velocity is not None:
self._write(_fmtv.format(*velocity[ia]))
if moment is not None:
self._write(_fmtm.format(moment[ia]))
[docs]
@sile_fh_open()
def read_lattice(self) -> Lattice:
"""Reads supercell object from the file"""
self.fh.seek(0)
# read until "lattice_vector" is found
cell = []
for line in self:
if line.startswith("lattice_vector"):
cell.append([float(f) for f in line.split()[1:]])
return Lattice(cell)
[docs]
@sile_fh_open()
@deprecate_argument(
"velocity",
"ret_velocity",
"use ret_velocity= instead of veloticy=",
"0.15",
"0.16",
)
@deprecate_argument(
"moment",
"ret_moment",
"use ret_moment= instead of moment=",
"0.15",
"0.16",
)
def read_geometry(
self, ret_velocity: bool = False, ret_moment: bool = False
) -> Geometry:
"""Reads Geometry object from the file
Parameters
----------
ret_velocity: bool, optional
also return the velocities in the file, if not present, it will
return a 0 array
ret_moment: bool, optional
also return the moments specified in the file, if not present, it will
return a 0 array
Returns
-------
geometry : Geometry
geometry found in file
velocity : numpy.ndarray
array of velocities in Ang/ps for each atom, will only be returned if `ret_velocity` is true
moment : numpy.ndarray
array of initial moments of each atom, will only be returned if `ret_moment` is true
"""
lattice = self.read_lattice()
self.fh.seek(0)
sp = []
xyz = []
v = []
m = []
def ensure_length(l, length, add):
if length < 0:
raise SileError(
"Found a velocity/initial_moment entry before an atom entry?"
)
while len(l) < length:
l.append(add)
for line in self:
line = line.split()
if line[0] == "atom":
xyz.append([float(f) for f in line[1:4]])
elif line[0] == "atom_frac":
xyz.append([float(f) for f in line[1:4]] @ lattice.cell)
elif line[0] == "velocity":
# ensure xyz and v are same length
ensure_length(v, len(xyz) - 1, [0, 0, 0])
v.append([float(f) for f in line[1:4]])
continue
elif line[0] == "initial_moment":
# ensure xyz and v are same length
ensure_length(m, len(xyz) - 1, 0)
m.append(float(line[1]))
continue
else:
continue
# we found an atom
sp.append(line[4])
ret = (Geometry(xyz, atoms=sp, lattice=lattice),)
if not ret_velocity and not ret_moment:
return ret[0]
if ret_velocity:
ret = ret + (np.array(v),)
if ret_moment:
ret = ret + (np.array(m),)
return ret
[docs]
def read_velocity(self) -> np.ndarray:
"""Reads velocity in the file"""
return self.read_geometry(ret_velocity=True)[1]
[docs]
def read_moment(self) -> np.ndarray:
"""Reads initial moment in the file"""
return self.read_geometry(ret_moment=True)[1]
def ArgumentParser(self, p=None, *args, **kwargs):
"""Returns the arguments that is available for this Sile"""
newkw = Geometry._ArgumentParser_args_single()
newkw.update(kwargs)
return self.read_geometry().ArgumentParser(p, *args, **newkw)
add_sile("geometry.in", inSileFHIaims, case=False, gzip=True)
add_sile("aims", inSileFHIaims, case=False, gzip=True)