Skip to content

Commit

Permalink
fix: skipping of char and int64 sub blocks and mock tests
Browse files Browse the repository at this point in the history
This adds some artificially created files to test all sub-block types
and various error messages.
  • Loading branch information
ezavod committed Oct 27, 2023
1 parent 390ca2a commit 08d459b
Show file tree
Hide file tree
Showing 15 changed files with 369 additions and 13 deletions.
26 changes: 13 additions & 13 deletions pyedr/pyedr/pyedr.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,9 @@ def do_eheader(self):
fr.nblock = data.unpack_int()
assert fr.nblock >= 0
if ndisre != 0:
if file_version >= 4:
raise ValueError("Distance restraint blocks "
"in old style in new style file")
# Distance restraint blocks of old style in new style file
# ndisre is only read from file for older than 4 versions
assert file_version < 4
fr.nblock += 1
# we now know what these should be,
# or we've already bailed out because
Expand Down Expand Up @@ -261,10 +261,10 @@ def do_enx(self):
frametime = 0
try:
self.do_eheader()
except ValueError:
except ValueError as e:
print("Last energy frame read {} time {:8.3f}".format(framenr - 1,
frametime))
raise RuntimeError()
raise RuntimeError("Failed reading header") from e
framenr += 1
frametime = fr.t

Expand Down Expand Up @@ -347,12 +347,11 @@ def convert_full_sums(self):
ener_prev[i].eav = eav_all
nsum_prev = nstep_all
elif fr.nsum > 0:
if fr.nsum != nstep_all:
warnings.warn('WARNING: something is wrong with the '
'energy sums, will not use exact averages')
nsum_prev = 0
else:
nsum_prev = nstep_all
# Conversion is only done for version 1 files
# For those files, fr.nsum is always set
# to nstep_all while parsing the header
assert fr.nsum == nstep_all
nsum_prev = nstep_all
# Copy all sums to ener_prev
for i in range(fr.nre):
ener_prev[i].esum = fr.ener[i].esum
Expand Down Expand Up @@ -451,12 +450,13 @@ def ndo_double(data, n):

def ndo_int64(data, n):
"""mimic of gmx_fio_ndo_int64 in gromacs"""
return [data.unpack_huge() for i in range(n)]
return [data.unpack_hyper() for i in range(n)]


def ndo_char(data, n):
"""mimic of gmx_fio_ndo_char in gromacs"""
return [data.unpack_char() for i in range(n)]
# Note: chars are encoded as int(32)
return [data.unpack_int() for i in range(n)]


def ndo_string(data, n):
Expand Down
167 changes: 167 additions & 0 deletions pyedr/pyedr/tests/data/mocks/edr_mock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# -*- coding: utf-8 -*-
# Helper script to generate mock EDR files with manipulated values. This allows
# setting some values in EDR files in such a way that certain conditions can be
# triggered while parsing. Note that not all parameter combinations are
# sensible and produce valid EDR files.

import xdrlib
from dataclasses import dataclass

MAGIC = -55555 # File magic number
ENX_VERSION = 4 # File version
assert ENX_VERSION >= 1
NSUM = 0 # Value of fr.nsum
# Column names with associated units (units are ignored for version 1)
# These values are parsed in do_enxnms and stored as fr.nms
NMS = [("DUMMY1", "UNIT1"), ("DUMMY2", "UNIT2")]
NRE = len(NMS) # Derive number of columns from NMS
FIRST_REAL = -1.0 # Value of first_real_to_check in do_eheader
FRAME_MAGIC = -7777777 # Frame magic number
NSTEPS = 3 # Number of steps to write
# Time step between frames
# Frame times are simply set to step*DT
DT = 0.5
NDISRE = 0 # Number of distance restraints (can be > 0 for versions < 4)
E_SIZE = 0 # Value to write as fr.e_size


@dataclass
class Block:
bid: int # block id
sub: [] # list of sub blocks


@dataclass
class SubBlock:
nr: int # number of values stored in sub block
btype: int # type of stored values


# Option 1: Write no other data blocks

BLOCKS = []

# Option 2: Write blocks of every possible sub-block type

# BLOCKS = []
# for t in range(6):
# bid = t + 3 # generate some arbitrary block id
# sub_nr1 = 7 - t # and nr of values in the sub block
# sub_nr2 = 6 - t
# BLOCKS.append(Block(bid, [SubBlock(sub_nr1, t), SubBlock(sub_nr2, t)]))

# Option 3: Write a block with an invalid sub-block type (> 5)

# BLOCKS = [Block(7, [SubBlock(2, 0), SubBlock(1, 1_000_000_000)])]

# Option 4: Write some additional distance restraints
# and optionally some more blocks
# Version must be set to < 4
# Note from pyedr: blocks in old version files always have 1 subblock
# that consists of reals

# NDISRE = 2
# BLOCKS = []
# for i in range(3):
# BLOCKS.append(Block(i, [SubBlock(4, 1)]))
# ENX_VERSION = 3

NBLOCK = len(BLOCKS)

p = xdrlib.Packer()

# do_enxnms
if ENX_VERSION == 1:
p.pack_int(NRE)
else:
p.pack_int(MAGIC)
p.pack_int(ENX_VERSION)
p.pack_int(NRE)
for nm, u in NMS:
p.pack_string(nm.encode("ascii"))
if ENX_VERSION >= 2:
p.pack_string(u.encode("ascii"))

for step in range(NSTEPS):
t = step * DT # Just set some value for fr.t
# do_enx
# -> do_eheader
if ENX_VERSION == 1:
p.pack_float(t)
p.pack_int(step)
else:
p.pack_float(FIRST_REAL)
p.pack_int(FRAME_MAGIC)
p.pack_int(ENX_VERSION)
p.pack_double(t)
p.pack_hyper(step)
p.pack_int(NSUM)
if ENX_VERSION >= 3:
p.pack_hyper(NSTEPS)
if ENX_VERSION >= 5:
p.pack_double(DT)
p.pack_int(NRE)
p.pack_int(NDISRE)
p.pack_int(NBLOCK)
if NDISRE != 0:
assert ENX_VERSION < 4

frame_blocks = BLOCKS.copy()
startb = 0
if NDISRE > 0:
enxDISRE = 3 # Some constant defined by Gromacs
# Sub-block type is 1 = float
frame_blocks.insert(
0, Block(enxDISRE, [SubBlock(NDISRE, 1), SubBlock(NDISRE, 1)])
)
startb += 1
for b in range(startb, len(frame_blocks)):
if ENX_VERSION < 4:
# Old versions have only one sub block of reals (here 1 = float)
assert len(frame_blocks[b].sub) == 1
assert frame_blocks[b].sub[0].btype == 1
p.pack_int(frame_blocks[b].sub[0].nr)
else:
p.pack_int(frame_blocks[b].bid)
p.pack_int(len(frame_blocks[b].sub))
for sub in frame_blocks[b].sub:
p.pack_int(sub.btype)
p.pack_int(sub.nr)

p.pack_int(E_SIZE)
p.pack_int(0) # dummy
p.pack_int(0) # dummy
# <- do_eheader
for i in range(NRE):
# Just generate some arbitrary value for the energy
# Depends on step and column number
p.pack_float(step * 100 + i) # e
if ENX_VERSION == 1 or NSUM > 0:
p.pack_float(0.0) # eav
p.pack_float(0.0) # esum
if ENX_VERSION == 1:
p.pack_float(0.0) # dummy

for b in range(len(frame_blocks)):
for sub in frame_blocks[b].sub:
for n in range(sub.nr):
if sub.btype == 0:
p.pack_int(0)
elif sub.btype == 1:
p.pack_float(0.0)
elif sub.btype == 2:
p.pack_double(0.0)
elif sub.btype == 3:
p.pack_hyper(0)
elif sub.btype == 4:
# GMX casts the char to an u8 and writes this as an u32
p.pack_int(0)
elif sub.btype == 5:
p.pack_string("ABC".encode("ascii"))
else:
print("WARNING: Unknown sub-block type")
p.pack_int(0)


with open("dump.edr", "wb") as f:
f.write(p.get_buffer())
Binary file added pyedr/pyedr/tests/data/mocks/v1_nre2_esum0.edr
Binary file not shown.
Binary file added pyedr/pyedr/tests/data/mocks/v1_step_negative.edr
Binary file not shown.
Binary file added pyedr/pyedr/tests/data/mocks/v3_ndisre2_blocks.edr
Binary file not shown.
Binary file not shown.
Binary file added pyedr/pyedr/tests/data/mocks/v4_first_real_v1.edr
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added pyedr/pyedr/tests/data/mocks/v5_step_negative.edr
Binary file not shown.
Binary file added pyedr/pyedr/tests/data/mocks/v_large.edr
Binary file not shown.
30 changes: 30 additions & 0 deletions pyedr/pyedr/tests/datafiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,33 @@
(EDR_V4_DOUBLE, EDR_V4_DOUBLE_XVG, EDR_V4_DOUBLE_UNITS, 4),
(Path(EDR), EDR_XVG, EDR_UNITS, 5),
]

EDR_MOCK_V1_ESUM0 = resource_filename(__name__, 'data/mocks/v1_nre2_esum0.edr')
EDR_MOCK_V5_STEP_NEGATIVE = resource_filename(
__name__, 'data/mocks/v5_step_negative.edr'
)
EDR_MOCK_V1_STEP_NEGATIVE = resource_filename(
__name__, 'data/mocks/v1_step_negative.edr'
)
EDR_MOCK_V_LARGE = resource_filename(__name__, 'data/mocks/v_large.edr')
EDR_MOCK_V4_LARGE_VERSION_FRAME = resource_filename(
__name__, 'data/mocks/v4_large_version_frame.edr'
)
EDR_MOCK_V4_FIRST_REAL_V1 = resource_filename(
__name__, 'data/mocks/v4_first_real_v1.edr'
)
EDR_MOCK_V4_INVALID_FILE_MAGIC = resource_filename(
__name__, 'data/mocks/v4_invalid_file_magic.edr'
)
EDR_MOCK_V4_INVALID_FRAME_MAGIC = resource_filename(
__name__, 'data/mocks/v4_invalid_frame_magic.edr'
)
EDR_MOCK_V4_INVALID_BLOCK_TYPE = resource_filename(
__name__, 'data/mocks/v4_invalid_block_type.edr'
)
EDR_MOCK_V4_ALL_BLOCK_TYPES = resource_filename(
__name__, 'data/mocks/v4_all_block_types.edr'
)
EDR_MOCK_V3_NDISRE2_BLOCKS = resource_filename(
__name__, 'data/mocks/v3_ndisre2_blocks.edr'
)
Loading

0 comments on commit 08d459b

Please sign in to comment.