From 8452bf4c0eefb8b182b2cb853217698d501e4390 Mon Sep 17 00:00:00 2001 From: Wei-Tse Hsu Date: Wed, 17 Apr 2024 16:14:22 +0800 Subject: [PATCH] Movied FileUtils to utils.py --- ensemble_md/tests/test_gmx_parser.py | 5 -- ensemble_md/utils/gmx_parser.py | 83 +--------------------------- ensemble_md/utils/utils.py | 44 ++++++++++++++- 3 files changed, 45 insertions(+), 87 deletions(-) diff --git a/ensemble_md/tests/test_gmx_parser.py b/ensemble_md/tests/test_gmx_parser.py index 1a929645..324eaf53 100644 --- a/ensemble_md/tests/test_gmx_parser.py +++ b/ensemble_md/tests/test_gmx_parser.py @@ -23,11 +23,6 @@ def test_parse_log(): - """ - - Case 1: The weights have never been equilibrated. - - Case 2: The weights were equilibrated during the simulation. - - Case 3: The weights were fixed in the simulation. - """ # Case 1: weight-updating simulation weights_0, counts_0, wl_delta_0, equil_time_0 = gmx_parser.parse_log(os.path.join(input_path, 'log/EXE_0.log')) assert len(weights_0) == 5 diff --git a/ensemble_md/utils/gmx_parser.py b/ensemble_md/utils/gmx_parser.py index 7c153604..49814464 100644 --- a/ensemble_md/utils/gmx_parser.py +++ b/ensemble_md/utils/gmx_parser.py @@ -10,7 +10,6 @@ """ The :code:`gmx_parser` module provides functions for parsing GROMACS files. """ -import os import re import six import logging @@ -170,83 +169,7 @@ def parse_log(log_file): return weights, counts, wl_delta, equil_time -class FileUtils(object): - """Mixin class to provide additional file-related capabilities. - Modified from `utilities.py in GromacsWrapper `_. - Copyright (c) 2009 Oliver Beckstein - """ - - #: Default extension for files read/written by this class. - default_extension = None - - def _init_filename(self, filename=None, ext=None): - """Initialize the current filename :attr:`FileUtils.real_filename` of the object. - - Bit of a hack. - - - The first invocation must have ``filename != None``; this will set a - default filename with suffix :attr:`FileUtils.default_extension` - unless another one was supplied. - - - Subsequent invocations either change the filename accordingly or - ensure that the default filename is set with the proper suffix. - - """ - - extension = ext or self.default_extension - filename = self.filename( - filename, ext=extension, use_my_ext=True, set_default=True - ) - #: Current full path of the object for reading and writing I/O. - self.real_filename = os.path.realpath(filename) - - def filename(self, filename=None, ext=None, set_default=False, use_my_ext=False): - """Supply a file name for the class object. - - Typical uses:: - - fn = filename() ---> - fn = filename('name.ext') ---> 'name' - fn = filename(ext='pickle') ---> '.pickle' - fn = filename('name.inp','pdf') --> 'name.pdf' - fn = filename('foo.pdf',ext='png',use_my_ext=True) --> 'foo.pdf' - - The returned filename is stripped of the extension - (``use_my_ext=False``) and if provided, another extension is - appended. Chooses a default if no filename is given. - - Raises a ``ValueError`` exception if no default file name is known. - - If ``set_default=True`` then the default filename is also set. - - ``use_my_ext=True`` lets the suffix of a provided filename take - priority over a default ``ext`` tension. - """ - if filename is None: - if not hasattr(self, "_filename"): - self._filename = None # add attribute to class - if self._filename: - filename = self._filename - else: - raise ValueError( - "A file name is required because no default file name was defined." - ) - my_ext = None - else: - filename, my_ext = os.path.splitext(filename) - if set_default: # replaces existing default file name - self._filename = filename - if my_ext and use_my_ext: - ext = my_ext - if ext is not None: - if ext.startswith(os.extsep): - ext = ext[1:] # strip a dot to avoid annoying mistakes - if ext != "": - filename = filename + os.extsep + ext - return filename - - -class MDP(odict, FileUtils): +class MDP(odict, utils.FileUtils): """Class that represents a Gromacs mdp run input file. Modified from `GromacsWrapper `_. Copyright (c) 2009-2011 Oliver Beckstein @@ -304,13 +227,13 @@ def __init__(self, filename=None, autoconvert=True, **kwargs): def __eq__(self, other): """ - __eq__ inherited from FileUtils needs to be overridden if new attributes (autoconvert in + __eq__ inherited from utils.FileUtils needs to be overridden if new attributes (autoconvert in this case) are assigned to the instance of the subclass (MDP in our case). See `this post by LGTM `_ for more details. """ if not isinstance(other, MDP): return False - return FileUtils.__eq__(self, other) and self.autoconvert == other.autoconvert + return utils.FileUtils.__eq__(self, other) and self.autoconvert == other.autoconvert def _transform(self, value): if self.autoconvert: diff --git a/ensemble_md/utils/utils.py b/ensemble_md/utils/utils.py index 99d40ca9..c632b1b7 100644 --- a/ensemble_md/utils/utils.py +++ b/ensemble_md/utils/utils.py @@ -10,6 +10,7 @@ """ The :obj:`.utils` module provides useful utility functions for running or analyzing REXEE simulations. """ +import os import sys import glob import natsort @@ -63,6 +64,45 @@ def flush(self): pass +class FileUtils: + """ + A utility class for managing file names and extensions within file operations. + + Attributes + ---------- + real_filename : str + The full path of the object for reading and writing I/O. + """ + default_extension = None + + def _init_filename(self, filename=None, ext=None): + extension = ext or self.default_extension + filename = self.filename(filename, ext=extension, use_my_ext=True, set_default=True) + self.real_filename = os.path.realpath(filename) + + def filename(self, filename=None, ext=None, set_default=False, use_my_ext=False): + if filename is None: + if not hasattr(self, "_filename"): + self._filename = None # add attribute to class + if self._filename: + filename = self._filename + else: + raise ValueError("A file name is required because no default file name was defined.") + my_ext = None + else: + filename, my_ext = os.path.splitext(filename) + if set_default: # replaces existing default file name + self._filename = filename + if my_ext and use_my_ext: + ext = my_ext + if ext is not None: + if ext.startswith(os.extsep): + ext = ext[1:] # strip a dot to avoid annoying mistakes + if ext != "": + filename = filename + os.extsep + ext + return filename + + def run_gmx_cmd(arguments, prompt_input=None): """ Runs a GROMACS command through a subprocess call. @@ -296,8 +336,8 @@ def analyze_REXEE_time(n_iter=None, log_files=None): iterations by counting the number of directories named in the format of :code`iteration_*` in the simulation directory (specifically :code:`sim_0`) in the current working directory or where the log files are located. log_files : None or list, Optional - A list of lists of log paths with the shape of :code:`(n_iter, n_replicas)`. If None, the function will try to find - the log files by searching the current working directory. + A list of lists of log paths with the shape of :code:`(n_iter, n_replicas)`. If None, the function will try to + find the log files by searching the current working directory. Returns -------