Robotics
code
Silk_LLM / runner.py
nlsefouh's picture
Upload 8 files
aac5fad verified
"""Functions to read and write RuNNer data files."""
__all__ = [
'write_frame_runner',
'read_frame_runner',
]
import numpy as np
from .utilities import Frame, register_io
@register_io('RuNNer', 'read', 'data') # noqa: C901
def read_frame_runner(f_in):
"""Read one frame of the RuNNer format from an open file.
Arguments:
f_in: open file in the RuNNer format
Returns:
`Frame` instance or `None`
"""
# For reference, in n2p2, this is implemented in `Structure::readFromFile`, found somewhere here:
# https://github.com/CompPhysVienna/n2p2/blob/master/src/libnnp/Structure.cpp#L84
# read first line to examine it
line_begin = f_in.readline()
# no more data in the file
if not line_begin:
return None
# there is some data, frame should start with 'begin'
if line_begin.strip() != 'begin':
raise ValueError
comment = None
cell = []
names = []
positions = []
forces = []
energy = None
for line in f_in:
items = line.split()
tag = items[0]
if tag == 'comment':
comment = " ".join(items[1:])
elif tag == 'lattice':
cell.append([float(item) for item in items[1:]])
elif tag == 'atom':
positions.append([float(item) for item in items[1:4]])
names.append(items[4])
forces.append([float(item) for item in items[7:10]])
# items[5] is atomic energy, only RuNNer itself (potentially) deals with that
# items[6] is atomic energy - not really used by anyone
elif tag == 'energy':
energy = float(items[1])
elif tag == 'charge':
pass
elif tag == 'end':
break
else:
raise ValueError('Unexpected data in file.')
if len(names) == 0:
raise ValueError('No atomic data.')
cell = np.array(cell)
if cell.shape != (3, 3) and len(cell) != 0:
raise ValueError('Wrong cell data.')
if len(cell) == 0:
cell = None
positions = np.array(positions)
forces = np.array(forces)
# Prepare frame
frame = Frame(names=names, positions=positions, comment=comment, cell=cell, energy=energy, forces=forces)
return frame
@register_io('RuNNer', 'write', 'data')
def write_frame_runner(f_out, frame):
# "cell" and "lattice" is the same data, we just use the terminology of the file format here.
#
# Note that atomic charges, atomic energies, and total charge currently not supported
# and zeros will be written in the file for these.
# Check that required data is in the frame:
if (frame.positions is None) or (frame.names is None):
raise ValueError('Frame does not contain required properties - atom names and positions.')
fmt_lattice = 'lattice ' + 3*'{:16.6f}' + '\n'
fmt_one = '{:13.6f}'
fmt_atom = 'atom ' + 3*fmt_one + '{:^6s}' + 5*fmt_one + '\n'
fmt_energy = 'energy ' + fmt_one + '\n'
fmt_charge = 'charge ' + fmt_one + '\n'
f_out.write('begin\n')
if frame.comment is not None:
f_out.write('comment ' + frame.comment + '\n')
if frame.cell is not None:
for lattice_vector in frame.cell:
f_out.write(fmt_lattice.format(*lattice_vector))
if frame.forces is not None:
for i, name in enumerate(frame.names):
f_out.write(fmt_atom.format(*frame.positions[i], name,
0.0, 0.0, *frame.forces[i]))
else:
for i, name in enumerate(frame.names):
f_out.write(fmt_atom.format(*frame.positions[i], name,
0.0, 0.0, 0.0, 0.0, 0.0))
if frame.energy is None:
energy = 0.0
else:
energy = frame.energy
f_out.write(fmt_energy.format(energy))
f_out.write(fmt_charge.format(0.0))
f_out.write('end\n')