From 471572acbc518aee19cc42093645f65800abda5a Mon Sep 17 00:00:00 2001 From: alescoulie Date: Thu, 22 Jul 2021 17:51:44 -0700 Subject: [PATCH 01/56] Update tests for python 3 compatibility --- mdpow/tests/test_analysis.py | 9 ++-- mdpow/tests/test_analysis_alchemlyb.py | 9 ++-- mdpow/tests/test_equilibration_script.py | 2 +- mdpow/tests/test_fep_script.py | 9 ++-- mdpow/tests/test_forcefields.py | 57 ++++++++++++------------ mdpow/tests/test_solvation.py | 12 ++--- 6 files changed, 47 insertions(+), 51 deletions(-) diff --git a/mdpow/tests/test_analysis.py b/mdpow/tests/test_analysis.py index 028caadf..62746eb9 100644 --- a/mdpow/tests/test_analysis.py +++ b/mdpow/tests/test_analysis.py @@ -62,10 +62,11 @@ def fep_benzene_directory(tmpdir_factory): class TestAnalyze(object): def get_Gsolv(self, pth): gsolv = pth.join("FEP", "water", "Gsolv.fep") - G = pickle.load(gsolv.open()) - # patch paths - G.basedir = pth.strpath - G.filename = gsolv.strpath + with open(gsolv, 'rb') as f: + G = pickle.load(f, encoding='latin1') + # patch paths + G.basedir = pth.strpath + G.filename = gsolv.strpath return G @staticmethod diff --git a/mdpow/tests/test_analysis_alchemlyb.py b/mdpow/tests/test_analysis_alchemlyb.py index b431f525..d5ed5bb8 100644 --- a/mdpow/tests/test_analysis_alchemlyb.py +++ b/mdpow/tests/test_analysis_alchemlyb.py @@ -62,10 +62,11 @@ def fep_benzene_directory(tmpdir_factory): class TestAnalyze(object): def get_Gsolv(self, pth): gsolv = pth.join("FEP", "water", "Gsolv.fep") - G = pickle.load(gsolv.open()) - # patch paths - G.basedir = pth.strpath - G.filename = gsolv.strpath + with open(gsolv, 'rb') as f: + G = pickle.load(f, encoding='latin1') + # patch paths + G.basedir = pth.strpath + G.filename = gsolv.strpath return G @pytest.mark.parametrize('method, Gibbs, coulomb, vdw', [ diff --git a/mdpow/tests/test_equilibration_script.py b/mdpow/tests/test_equilibration_script.py index a031f206..8670513f 100644 --- a/mdpow/tests/test_equilibration_script.py +++ b/mdpow/tests/test_equilibration_script.py @@ -13,7 +13,7 @@ def setup(self): self.tmpdir = td.TempDir() self.old_path = os.getcwd() self.resources = os.path.join( - self.old_path, 'mdpow', 'tests', 'testing_resources') + self.old_path, 'testing_resources') m = pybol.Manifest(os.path.join(self.resources, 'manifest.yml')) m.assemble('base', self.tmpdir.name) diff --git a/mdpow/tests/test_fep_script.py b/mdpow/tests/test_fep_script.py index 50af2da1..68ca8168 100644 --- a/mdpow/tests/test_fep_script.py +++ b/mdpow/tests/test_fep_script.py @@ -11,10 +11,9 @@ class TestFEPScript(object): def setup(self): self.tmpdir = td.TempDir() self.old_path = os.getcwd() - self.resources = os.path.join( - self.old_path, 'mdpow', 'tests', 'testing_resources') - self.m = pybol.Manifest(os.path.join(self.resources,'manifest.yml')) - self.m.assemble('md_npt',self.tmpdir.name) + self.resources = os.path.join(self.old_path, 'testing_resources') + self.m = pybol.Manifest(os.path.join(self.resources, 'manifest.yml')) + self.m.assemble('md_npt', self.tmpdir.name) S = Simulation(filename=os.path.join( self.tmpdir.name, 'benzene', 'water.simulation')) @@ -35,7 +34,7 @@ def test_default_run(self): try: self._run_fep('water', 'benzene/') except: - raise AssertionError('FEP simulations failed with exception:\n{0}'.format(str(err))) + raise AssertionError('FEP simulations failed with exception:\n{0}') assert os.path.exists(os.path.join(self.tmpdir.name, 'benzene', 'FEP', 'water', 'VDW', '0000', 'md.edr')) diff --git a/mdpow/tests/test_forcefields.py b/mdpow/tests/test_forcefields.py index bc015e3f..01af9593 100644 --- a/mdpow/tests/test_forcefields.py +++ b/mdpow/tests/test_forcefields.py @@ -1,8 +1,7 @@ import os.path import pytest - -import mdpow.config +from mdpow.config import topfiles import mdpow.forcefields # currently supported @@ -16,14 +15,14 @@ def test_default_forcefield(): @staticmethod def test_oplsaa_itp(): - assert "ffoplsaa.itp" in mdpow.config.topfiles - assert mdpow.config.topfiles["ffoplsaa.itp"].endswith( + assert "ffoplsaa.itp" in topfiles + assert topfiles["ffoplsaa.itp"].endswith( os.path.join('mdpow', 'top', 'ffoplsaa.itp')) @staticmethod def test_oplsaa_ff(): - assert "oplsaa.ff" in mdpow.config.topfiles - assert mdpow.config.topfiles["oplsaa.ff"].endswith( + assert "oplsaa.ff" in topfiles + assert topfiles["oplsaa.ff"].endswith( os.path.join('mdpow', 'top', 'oplsaa.ff')) class TestIncludedSolvents(object): @@ -49,9 +48,9 @@ class TestIncludedSolvents(object): def _test_solvent(self, name): solvent = self.solvents[name] def _assert_has_filename(filename): - assert filename in mdpow.config.topfiles + assert filename in topfiles def _assert_correct_path(filename, path): - assert mdpow.config.topfiles[filename].endswith(path) + assert topfiles[filename].endswith(path) for filename, path in solvent: yield _assert_has_filename, filename @@ -73,27 +72,28 @@ class TestWatermodels(object): def test_default_water_model(): assert mdpow.forcefields.DEFAULT_WATER_MODEL == "tip4p" - def test_watermodelsdat(self): - included_watermodels = open(mdpow.config.topfiles['watermodels.dat']).read() - for line, ref in zip(self._simple_line_parser(mdpow.forcefields.GMX_WATERMODELS_DAT), - self._simple_line_parser(included_watermodels)): - assert line.strip() == ref.strip() - - def test_gromacs_water_models(self): + @pytest.mark.parametrize('expected', WATERMODELS) + def test_gromacs_water_models(self, expected): models = mdpow.forcefields.GROMACS_WATER_MODELS def has_identifier(identifier): assert identifier in models def itp_in_top(identifier): model = models[identifier] - assert model.itp in mdpow.config.topfiles + assert model.itp in topfiles + def coordinates_in_top(identifier): model = models[identifier] - assert model.coordinates in mdpow.config.topfiles + assert model.coordinates in topfiles + + has_identifier(expected) + itp_in_top(expected) + coordinates_in_top(expected) - for identifier in self.watermodels: - yield has_identifier, identifier - yield itp_in_top, identifier - yield coordinates_in_top, identifier + def test_watermodelsdat(self): + included_watermodels = open(topfiles['watermodels.dat']).read() + for line, ref in zip(self._simple_line_parser(mdpow.forcefields.GMX_WATERMODELS_DAT), + self._simple_line_parser(included_watermodels)): + assert line.strip() == ref.strip() @staticmethod def _simple_line_parser(string): @@ -157,23 +157,22 @@ def test_get_solvent_identifier_default_is_water(): assert (mdpow.forcefields.get_solvent_identifier('water') is mdpow.forcefields.DEFAULT_WATER_MODEL) - def test_get_solvent_identifier_water(self): + @pytest.mark.parametrize('test_model', WATERMODELS) + def test_get_solvent_identifier_water(self, test_model): def _assert_model(model): assert mdpow.forcefields.get_solvent_identifier('water', model=model) is model - for model in self.watermodels: - yield _assert_model, model + _assert_model(test_model) - def test_get_solvent_identifier_solvents(self): + @pytest.mark.parametrize('test_solvent', [model for model in SOLVENTMODELS if model != "water"]) + def test_get_solvent_identifier_solvents(self, test_solvent): def _assert_model(solvent, model): assert mdpow.forcefields.get_solvent_identifier(solvent, model=model) is solvent - for solvent in self.solventmodels: - yield _assert_model, solvent, None + _assert_model(test_solvent, None) # make sure that model is ignored - for solvent in self.solventmodels: - yield _assert_model, solvent, "Jabberwock model" + _assert_model(test_solvent, "Jabberwock model") @staticmethod def test_get_solvent_identifier_None(): diff --git a/mdpow/tests/test_solvation.py b/mdpow/tests/test_solvation.py index e4d4da8f..4acedc01 100644 --- a/mdpow/tests/test_solvation.py +++ b/mdpow/tests/test_solvation.py @@ -3,7 +3,6 @@ import mdpow.equil from gromacs.utilities import in_dir -import gromacs import pytest @@ -20,10 +19,10 @@ @pytest.fixture def setup(tmpdir): + os.system('echo') newdir = tmpdir.mkdir('resources') old_path = os.getcwd() - resources = os.path.join( - old_path, 'mdpow', 'tests', 'testing_resources') + resources = os.path.join(old_path, 'testing_resources') files = ['benzene.pdb', 'benzene.itp', 'benzene_charmm.itp', 'benzene_amber.itp'] for f in files: @@ -52,11 +51,8 @@ def test_solvation_octanol(setup, ff): def test_solvation_cyclohexane(setup): solvation(setup, "cyclohexane") -@pytest.mark.xfail(gromacs.release.startswith('4') - or gromacs.release.startswith('5') - or gromacs.release.startswith('2016'), - reason="GROMACS < 2018 cannot easily work with mixed solvents " - "(see issue #111)") + +@pytest.mark.xfail("gromacs.release().startswith('2019')") @pytest.mark.parametrize("ff", ['OPLS-AA', 'CHARMM', 'AMBER']) def test_solvation_wetoctanol(setup, ff): solvation(setup, "wetoctanol", ff) From 46fa8632bd0575fd5c285a6ab9b9138ab7ea05ce Mon Sep 17 00:00:00 2001 From: alescoulie Date: Thu, 22 Jul 2021 17:55:11 -0700 Subject: [PATCH 02/56] Update ci workflow for python 3.8 --- .github/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3034606e..382fb1ad 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -23,8 +23,8 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macOS-latest] - python-version: [2.7] - #python-version: [2.7, 3.8] + #python-version: [2.7] + python-version: [2.7, 3.8] #python-version: [2.7, 3.6, 3.7, 3.8, 3.9] gromacs-version: ["4.6.5", "2018.6", "2019.1", "2021.1"] # only test one GROMACS version on macOS to keep the testing From 35144163a7c3b3d9e59702bbf745ad2c3b98153a Mon Sep 17 00:00:00 2001 From: alescoulie Date: Thu, 22 Jul 2021 18:03:03 -0700 Subject: [PATCH 03/56] Update merge_dicts in config.py --- mdpow/config.py | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/mdpow/config.py b/mdpow/config.py index f1a41d64..b96b24ac 100644 --- a/mdpow/config.py +++ b/mdpow/config.py @@ -112,15 +112,17 @@ def merge_dicts(user, default): """Merge two dictionaries recursively. - - Based on https://stackoverflow.com/a/823240/334357 + Uses recursive method to accurately + merge nested dictionaries """ - if isinstance(user, dict) and isinstance(default, dict): - for k, v in default.iteritems(): - if k not in user: - user[k] = v - else: - user[k] = merge_dicts(user[k], v) + for key in default: + if key in user: + if isinstance(user[key], dict) and isinstance(default[key], dict): + merge_dicts(user[key], default[key]) + elif user[key] == default[key]: + pass + else: + user[key] = default[key] return user @@ -167,8 +169,13 @@ def get(self, section, option): Prior versions would convert case-insensitively (e.g. "NONE" and "none") """ - value = self.conf[section][option] - return value if value != "None" else None + + value = self.conf[section] + if isinstance(value, dict): + if value[option] != 'None': + return value[option] + else: + return None # TODO: # The YAML parser does automatic conversion: the following @@ -233,7 +240,7 @@ def get_configuration(filename=None): def modify_gromacs_environment(name, value): from gromacs.environment import flags if flags[name] != value: - logger.warn("Changing GromacsWrapper environment: flags[%(name)r] = %(value)r", vars()) + logger.warning("Changing GromacsWrapper environment: flags[%(name)r] = %(value)r", vars()) flags[name] = value def set_gromacsoutput(cfg): @@ -308,7 +315,7 @@ def get_templates(t): :Raises: :exc:`ValueError` if no file can be located. """ - return [_get_template(s) for s in utilities.asiterable(t)] + return [_get_template(s) for s in asiterable(t)] def _get_template(t): """Return a single template *t*.""" @@ -339,8 +346,8 @@ def _get_template(t): def iterable(obj): """Returns ``True`` if *obj* can be iterated over and is *not* a string.""" - if isinstance(obj, basestring): - return False # avoid iterating over characters of a string + if isinstance(obj, str): + return False # avoid iterating over characters of a string if hasattr(obj, 'next'): return True # any iterator will do @@ -416,6 +423,6 @@ def asiterable(obj): logger.info("Using the bundled force fields from GMXLIB=%(includedir)r.", vars()) logger.info("If required, override this behaviour by setting the environment variable GMXLIB yourself.") else: - logger.warn("Using user-supplied environment variable GMXLIB=%r to find force fields", os.environ['GMXLIB']) + logger.warning("Using user-supplied environment variable GMXLIB=%r to find force fields", os.environ['GMXLIB']) logger.info("(You can use the MDPOW default by executing 'unset GMXLIB' in your shell before running MDPOW.)") From 9dc7685057a813ea7f32451c321ee755f11af24d Mon Sep 17 00:00:00 2001 From: alescoulie Date: Thu, 22 Jul 2021 18:04:00 -0700 Subject: [PATCH 04/56] Update equil.py imports and exceptions --- mdpow/equil.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/mdpow/equil.py b/mdpow/equil.py index e14f6902..0982751f 100644 --- a/mdpow/equil.py +++ b/mdpow/equil.py @@ -33,8 +33,9 @@ import os, errno import shutil -import cPickle + import MDAnalysis as mda +from six.moves import cPickle as pickle try: import gromacs.setup, gromacs.cbook @@ -227,7 +228,7 @@ def save(self, filename=None): else: self.filename = filename with open(filename, 'wb') as f: - cPickle.dump(self, f, protocol=cPickle.HIGHEST_PROTOCOL) + pickle.dump(self, f) logger.debug("Instance pickled to %(filename)r" % vars()) def load(self, filename=None): @@ -238,7 +239,7 @@ def load(self, filename=None): logger.warning("No filename known, trying name %r", self.filename) filename = self.filename with open(filename, 'rb') as f: - instance = cPickle.load(f) + instance = pickle.load(f) self.__dict__.update(instance.__dict__) logger.debug("Instance loaded from %(filename)r" % vars()) @@ -273,8 +274,8 @@ def assinglet(m): self.mdp[key] = fn.replace(basedir, prefix) except AttributeError: pass - logger.warn("make_paths_relative(): check/manually adjust %s.dirs.includes = %r !", - self.__class__.__name__, self.dirs.includes) + logger.warning("make_paths_relative(): check/manually adjust %s.dirs.includes = %r !", + self.__class__.__name__, self.dirs.includes) def topology(self, itp='drug.itp', prm=None, **kwargs): """Generate a topology for compound *molecule*. @@ -305,7 +306,7 @@ def topology(self, itp='drug.itp', prm=None, **kwargs): itp = os.path.realpath(itp) _itp = os.path.basename(itp) - if prm==None: + if prm is None: prm_kw = '' else: prm = os.path.realpath(prm) @@ -466,8 +467,8 @@ def _MD(self, protocol, **kwargs): # so instead of fuffing with GMXLIB we just dump it into the directory try: shutil.copy(config.topfiles['residuetypes.dat'], self.dirs[protocol]) - except: - logger.warn("Failed to copy 'residuetypes.dat': mdrun will likely fail to write a final structure") + except IOError: + logger.warning("Failed to copy 'residuetypes.dat': mdrun will likely fail to write a final structure") self.journal.completed(protocol) return params @@ -610,7 +611,10 @@ def get_last_checkpoint(self): """Returns the checkpoint of the most advanced step in the protocol. Relies on md.gro being present from previous simulation, assumes that checkpoint is then present. """ - return self._lastnotempty([self.files[name] for name in self.checkpoints]).replace('.gro','.cpt') + if self._lastnotempty([self.files[name] for name in self.checkpoints]) is not None: + return self._lastnotempty([self.files[name] for name in self.checkpoints]).replace('.gro', '.cpt') + return None + class WaterSimulation(Simulation): """Equilibrium MD of a solute in a box of water.""" From 946204f29694f3075846a959c2f1fd0b39dd135a Mon Sep 17 00:00:00 2001 From: alescoulie Date: Thu, 22 Jul 2021 18:06:16 -0700 Subject: [PATCH 05/56] Update fep.py imports __deepcopy__ and exceptions --- mdpow/fep.py | 51 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/mdpow/fep.py b/mdpow/fep.py index 753deb5a..fcc0d94f 100644 --- a/mdpow/fep.py +++ b/mdpow/fep.py @@ -128,6 +128,8 @@ from subprocess import call import warnings +import sys + import numpy import pandas as pd @@ -230,7 +232,7 @@ def mdp_dict(self): @staticmethod def load(cfg, section): """Initialize a :class:`FEPschedule` from the *section* in the configuration *cfg*""" - + from configparser import NoOptionError keys = {} keys.update(FEPschedule.mdp_keywords) keys.update(FEPschedule.meta_keywords) @@ -244,16 +246,24 @@ def load(cfg, section): def getter(type, section, key): try: return cfg_get[type](section, key) - except ConfigParser.NoOptionError: + except NoOptionError: return None return FEPschedule((key, getter(keytype, section, key)) for key,keytype in keys.items() if getter(keytype, section, key) is not None) def __deepcopy__(self, memo): - x = type(self)() - for k,v in self.iteritems(): - x[k] = copy.deepcopy(v, memo) - return x + if sys.version_info.major == 3: + from collections.abc import Iterable + x = FEPschedule() + for k, v in self.items(): + if isinstance(k, Iterable) and isinstance(v, Iterable): + x[k] = copy.deepcopy(v) + return x + elif sys.version_info == 2: + x = FEPschedule() + for k, v in self.iteritems(): + x[k] = copy.deepcopy(v) + return x class Gsolv(Journalled): """Simulations to calculate and analyze the solvation free energy. @@ -504,7 +514,7 @@ def __init__(self, molecule=None, top=None, struct=None, method="BAR", **kwargs) wmsg = "Directory %(dirname)r already exists --- will overwrite " \ "existing files." % vars(self) warnings.warn(wmsg) - logger.warn(wmsg) + logger.warning(wmsg) # overrides pickle file so that we can run from elsewhere if not basedir is None: @@ -720,8 +730,8 @@ def dgdl_edr(self, *args): pattern = os.path.join(*args + (self.deffnm + '*.edr',)) edrs = glob(pattern) if not edrs: - logger.error("Missing dgdl.edr file %(pattern)r.", vars()) - raise IOError(errno.ENOENT, "Missing dgdl.edr file", pattern) + logger.error("Missing dgdl.edr file %(pattern)r.", vars()) + raise IOError(errno.ENOENT, "Missing dgdl.edr file", pattern) return [os.path.abspath(i) for i in edrs] def dgdl_tpr(self, *args): @@ -834,8 +844,8 @@ def compress_dgdl_xvg(self): # speed is similar to 'bzip2 -9 FILE' (using a 1 Mio buffer) # (Since GW 0.8, openany() does not take kwargs anymore so the write buffer cannot be # set anymore (buffering=1048576) so the performance might be lower in MDPOW >= 0.7.0) - with open(xvg, 'r', buffering=1048576) as source: - with openany(fnbz2, 'w') as target: + with open(xvg, 'rb', buffering=1048576) as source: + with openany(fnbz2, 'wb') as target: target.writelines(source) if os.path.exists(fnbz2) and os.path.exists(xvg): os.unlink(xvg) @@ -855,19 +865,20 @@ def contains_corrupted_xvgs(self): :attr:`Gsolv._corrupted` as dicts of dicts with the component as primary and the lambda as secondary key. """ - from itertools import izip + def _lencorrupted(xvg): try: return len(xvg.corrupted_lineno) except AttributeError: # backwards compatible (pre gw 0.1.10 are always ok) return 0 - except TypeError: # len(None): XVG.parse() has not been run yet - return 0 # ... so we cannot conclude that it does contain bad ones + except TypeError: # len(None): XVG.parse() has not been run yet + return 0 # ... so we cannot conclude that it does contain bad ones + corrupted = {} self._corrupted = {} # debugging ... for component, (lambdas, xvgs) in self.results.xvg.items(): corrupted[component] = numpy.any([(_lencorrupted(xvg) > 0) for xvg in xvgs]) - self._corrupted[component] = dict(((l, _lencorrupted(xvg)) for l,xvg in izip(lambdas, xvgs))) + self._corrupted[component] = dict(((l, _lencorrupted(xvg)) for l, xvg in zip(lambdas, xvgs))) return numpy.any([x for x in corrupted.values()]) def analyze(self, force=False, stride=None, autosave=True, ncorrel=25000): @@ -960,7 +971,7 @@ def analyze(self, force=False, stride=None, autosave=True, ncorrel=25000): self.convert_edr() self.collect(stride=stride, autosave=False) else: - logger.exception() + logger.exception(err) raise else: logger.info("Analyzing stored data.") @@ -1064,7 +1075,7 @@ def analyze_alchemlyb(self, SI=True, start=0, stop=None, stride=None, force=Fals self.convert_edr() self.collect_alchemlyb(SI, start, stop, stride, autosave=False) else: - logger.exception() + logger.exception(err) raise else: logger.info("Analyzing stored data.") @@ -1384,6 +1395,12 @@ def p_transfer(G1, G2, **kwargs): logger.info("The solvent is %s .", G.solvent_type) logger.info("Estimator is %s.", estimator) logger.info("Free energy calculation method is %s.", G.method) + + try: + G.results.DeltaA.Gibbs + G.logger_DeltaA0() + except (KeyError, AttributeError): # KeyError because results is a AttributeDict + logger.warning("Must analyze simulation because no hydration free energy data available...") if estimator == 'mdpow': G.analyze(**G_kwargs) elif estimator == 'alchemlyb': From 64734d6643dd6cba1daefd15facc53e644e7a42e Mon Sep 17 00:00:00 2001 From: alescoulie Date: Thu, 22 Jul 2021 18:06:57 -0700 Subject: [PATCH 06/56] Fix filelock.py --- mdpow/filelock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mdpow/filelock.py b/mdpow/filelock.py index 4f8a93cf..9bf69f7a 100644 --- a/mdpow/filelock.py +++ b/mdpow/filelock.py @@ -62,7 +62,7 @@ def acquire(self): while True: try: self.fd = os.open(self.lockfile, os.O_CREAT|os.O_EXCL|os.O_RDWR) - break; + break except OSError as e: if e.errno != errno.EEXIST: raise From 246b8f1ee9b597a3acd18f769ba60609921eca03 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Thu, 22 Jul 2021 18:08:19 -0700 Subject: [PATCH 07/56] Update restart.py imports --- mdpow/restart.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mdpow/restart.py b/mdpow/restart.py index e58248c8..ca1fa1fc 100644 --- a/mdpow/restart.py +++ b/mdpow/restart.py @@ -25,7 +25,7 @@ import os import errno -import cPickle +from six.moves import cPickle as pickle import logging logger = logging.getLogger('mdpow.checkpoint') @@ -240,7 +240,7 @@ def save(self, filename=None): else: self.filename = os.path.abspath(filename) with open(self.filename, 'wb') as f: - cPickle.dump(self, f, protocol=cPickle.HIGHEST_PROTOCOL) + pickle.dump(self, f) logger.debug("Instance pickled to %(filename)r" % vars(self)) def load(self, filename=None): @@ -260,6 +260,6 @@ def load(self, filename=None): logger.error(errmsg) raise ValueError(errmsg) with open(filename, 'rb') as f: - instance = cPickle.load(f) + instance = pickle.load(f) self.__dict__.update(instance.__dict__) logger.debug("Instance loaded from %(filename)r" % vars()) From 02482bc86fad639b929e3aec01052b3dc478acdb Mon Sep 17 00:00:00 2001 From: alescoulie Date: Thu, 22 Jul 2021 18:09:20 -0700 Subject: [PATCH 08/56] Update run.py imports and exceptions --- mdpow/run.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mdpow/run.py b/mdpow/run.py index b20c7367..2c3505cb 100644 --- a/mdpow/run.py +++ b/mdpow/run.py @@ -71,7 +71,7 @@ def setupMD(S, protocol, cfg): def get_mdp_files(cfg, protocols): """Get file names of MDP files from *cfg* for all *protocols*""" - import ConfigParser + import configparser mdpfiles = {} for protocol in protocols: @@ -81,7 +81,7 @@ def get_mdp_files(cfg, protocols): # skip anything for which we do not define sections, such as # the dummy run protocols mdp = None - except ConfigParser.NoOptionError: + except configparser.NoOptionError: # Should not happen... let's continue and wait for hard-coded defaults logger.error("No 'mdp' config file entry for protocol [%s]---check input files!", protocol) mdp = None @@ -147,7 +147,7 @@ def runMD_or_exit(S, protocol, params, cfg, **kwargs): logger.info("Now go and run %(protocol)s in directory %(dirname)r.", vars()) sys.exit(0) elif simulation_done is False: - logger.warn("Simulation %(protocol)s in directory %(dirname)r is incomplete (log=%)logfile)s).", vars()) + logger.warning("Simulation %(protocol)s in directory %(dirname)r is incomplete (log=%)logfile)s).", vars()) sys.exit(1) logger.info("Simulation %(protocol)s seems complete (log=%(logfile)s)", vars()) return simulation_done @@ -306,7 +306,7 @@ def fep_simulation(cfg, solvent, **kwargs): equil_savefilename = os.path.join(topdir, "%(solvent)s.simulation" % vars()) try: equil_S = EquilSimulation(filename=equil_savefilename) - except IOError, err: + except IOError as err: if err.errno == errno.ENOENT: logger.critical("Missing the equilibrium simulation %(equil_savefilename)r.", vars()) logger.critical("Run `mdpow-equilibrium -S %s %s' first!", solvent, "RUNINPUT.cfg") From 3c8b27f6e8b667e97af14af369ef6a2f9688068a Mon Sep 17 00:00:00 2001 From: alescoulie Date: Fri, 23 Jul 2021 12:55:27 -0700 Subject: [PATCH 09/56] Update tests to current --- mdpow/tests/test_analysis.py | 9 ++- mdpow/tests/test_analysis_alchemlyb.py | 2 +- mdpow/tests/test_fep_script.py | 4 +- mdpow/tests/test_forcefields.py | 105 +++++++++---------------- mdpow/tests/test_solvation.py | 8 +- 5 files changed, 50 insertions(+), 78 deletions(-) diff --git a/mdpow/tests/test_analysis.py b/mdpow/tests/test_analysis.py index 62746eb9..5d6fafd6 100644 --- a/mdpow/tests/test_analysis.py +++ b/mdpow/tests/test_analysis.py @@ -9,6 +9,7 @@ from six.moves import cPickle as pickle +import numkit import mdpow.fep from pkg_resources import resource_filename @@ -41,7 +42,7 @@ def fix_manifest(topdir): m = pybol.Manifest(new_manifest.strpath) """ - manifest = yaml.load(MANIFEST.open()) + manifest = yaml.safe_load(MANIFEST.open()) # simple heuristic: last element of the recorded manifest::path is the name # of the states directory, typically 'states' (from .../testing_resources/states) manifest['path'] = RESOURCES.join(os.path.basename(manifest['path'])).strpath @@ -86,7 +87,8 @@ def assert_DeltaA(G): def test_convert_edr(self, fep_benzene_directory): G = self.get_Gsolv(fep_benzene_directory) try: - G.analyze(force=True, autosave=False) + with pytest.warns(numkit.LowAccuracyWarning): + G.analyze(force=True, autosave=False) except IOError as err: raise AssertionError("Failed to auto-convert edr to xvg: {0}: {1}".format( err.strerror, err.filename)) @@ -101,7 +103,8 @@ def test_TI(self, fep_benzene_directory): # fep_benzene_directory locally scoped G.convert_edr() try: - G.analyze(force=True, autosave=False) + with pytest.warns(numkit.LowAccuracyWarning): + G.analyze(force=True, autosave=False) except IOError as err: raise AssertionError("Failed to convert edr to xvg: {0}: {1}".format( err.strerror, err.filename)) diff --git a/mdpow/tests/test_analysis_alchemlyb.py b/mdpow/tests/test_analysis_alchemlyb.py index d5ed5bb8..1ac6deb5 100644 --- a/mdpow/tests/test_analysis_alchemlyb.py +++ b/mdpow/tests/test_analysis_alchemlyb.py @@ -41,7 +41,7 @@ def fix_manifest(topdir): m = pybol.Manifest(new_manifest.strpath) """ - manifest = yaml.load(MANIFEST.open()) + manifest = yaml.safe_load(MANIFEST.open()) # simple heuristic: last element of the recorded manifest::path is the name # of the states directory, typically 'states' (from .../testing_resources/states) manifest['path'] = RESOURCES.join(os.path.basename(manifest['path'])).strpath diff --git a/mdpow/tests/test_fep_script.py b/mdpow/tests/test_fep_script.py index 68ca8168..26207163 100644 --- a/mdpow/tests/test_fep_script.py +++ b/mdpow/tests/test_fep_script.py @@ -33,8 +33,8 @@ def test_default_run(self): with in_dir(self.tmpdir.name, create=False): try: self._run_fep('water', 'benzene/') - except: - raise AssertionError('FEP simulations failed with exception:\n{0}') + except Exception as err: + raise AssertionError('FEP simulations failed with exception:\n{0}'.format(str(err))) assert os.path.exists(os.path.join(self.tmpdir.name, 'benzene', 'FEP', 'water', 'VDW', '0000', 'md.edr')) diff --git a/mdpow/tests/test_forcefields.py b/mdpow/tests/test_forcefields.py index 01af9593..db556f80 100644 --- a/mdpow/tests/test_forcefields.py +++ b/mdpow/tests/test_forcefields.py @@ -1,7 +1,8 @@ import os.path import pytest -from mdpow.config import topfiles + +import mdpow.config import mdpow.forcefields # currently supported @@ -15,14 +16,14 @@ def test_default_forcefield(): @staticmethod def test_oplsaa_itp(): - assert "ffoplsaa.itp" in topfiles - assert topfiles["ffoplsaa.itp"].endswith( + assert "ffoplsaa.itp" in mdpow.config.topfiles + assert mdpow.config.topfiles["ffoplsaa.itp"].endswith( os.path.join('mdpow', 'top', 'ffoplsaa.itp')) @staticmethod def test_oplsaa_ff(): - assert "oplsaa.ff" in topfiles - assert topfiles["oplsaa.ff"].endswith( + assert "oplsaa.ff" in mdpow.config.topfiles + assert mdpow.config.topfiles["oplsaa.ff"].endswith( os.path.join('mdpow', 'top', 'oplsaa.ff')) class TestIncludedSolvents(object): @@ -41,60 +42,36 @@ class TestIncludedSolvents(object): }, } - # using nosetest-style test generators.. .feel free to rewrite for - # py.test but I found the docs for parameterizing tests in py.test - # too complicated - - def _test_solvent(self, name): - solvent = self.solvents[name] - def _assert_has_filename(filename): - assert filename in topfiles - def _assert_correct_path(filename, path): - assert topfiles[filename].endswith(path) - - for filename, path in solvent: - yield _assert_has_filename, filename - yield _assert_correct_path, filename, path + @pytest.mark.parametrize("solvent_name", ["tip4p", "octanol", "cyclohexane"]) + def test_solvent(self, solvent_name): + solvent = self.solvents[solvent_name] + for filename, path in solvent.items(): + assert filename in mdpow.config.topfiles + assert mdpow.config.topfiles[filename].endswith(path) - def test_tip4p(self): - self._test_solvent('tip4p') - - def test_octanol(self): - self._test_solvent('octanol') - - def test_cyclohexane(self): - self._test_solvent('cyclohexane') class TestWatermodels(object): - watermodels = WATERMODELS - @staticmethod def test_default_water_model(): assert mdpow.forcefields.DEFAULT_WATER_MODEL == "tip4p" - @pytest.mark.parametrize('expected', WATERMODELS) - def test_gromacs_water_models(self, expected): - models = mdpow.forcefields.GROMACS_WATER_MODELS - def has_identifier(identifier): - assert identifier in models - def itp_in_top(identifier): - model = models[identifier] - assert model.itp in topfiles - - def coordinates_in_top(identifier): - model = models[identifier] - assert model.coordinates in topfiles - - has_identifier(expected) - itp_in_top(expected) - coordinates_in_top(expected) - def test_watermodelsdat(self): - included_watermodels = open(topfiles['watermodels.dat']).read() + included_watermodels = open(mdpow.config.topfiles['watermodels.dat']).read() for line, ref in zip(self._simple_line_parser(mdpow.forcefields.GMX_WATERMODELS_DAT), self._simple_line_parser(included_watermodels)): assert line.strip() == ref.strip() + @pytest.mark.parametrize('identifier', WATERMODELS) + def test_gromacs_water_models(self, identifier): + models = mdpow.forcefields.GROMACS_WATER_MODELS + + assert identifier in models + + model = models[identifier] + assert model.itp in mdpow.config.topfiles + assert model.coordinates in mdpow.config.topfiles + + @staticmethod def _simple_line_parser(string): for line in string.split('\n'): @@ -114,11 +91,7 @@ def test_get_water_model_ValueError(): with pytest.raises(ValueError): mdpow.forcefields.get_water_model("The Jabberwock is an imaginary beast.") - class TestSolventModels(object): - watermodels = WATERMODELS - solventmodels = [model for model in SOLVENTMODELS if model != "water"] - @staticmethod def test_get_solvent_default_water(): model = "water" @@ -139,15 +112,13 @@ def test_get_solvent_cyclohexane(): mdpow.forcefields.GROMACS_SOLVENT_MODELS[forcefield][model]) @pytest.mark.parametrize("forcefield", ['OPLS-AA', 'CHARMM', 'AMBER']) - @staticmethod - def test_get_solvent_octanol(forcefield): + def test_get_solvent_octanol(self, forcefield): model = 'octanol' assert (mdpow.forcefields.get_solvent_model(model, forcefield=forcefield) is mdpow.forcefields.GROMACS_SOLVENT_MODELS[forcefield][model]) @pytest.mark.parametrize("forcefield", ['OPLS-AA', 'CHARMM', 'AMBER']) - @staticmethod - def test_get_solvent_wetoctanol(forcefield): + def test_get_solvent_wetoctanol(self, forcefield): model = 'wetoctanol' assert (mdpow.forcefields.get_solvent_model(model, forcefield=forcefield) is mdpow.forcefields.GROMACS_SOLVENT_MODELS[forcefield][model]) @@ -157,22 +128,16 @@ def test_get_solvent_identifier_default_is_water(): assert (mdpow.forcefields.get_solvent_identifier('water') is mdpow.forcefields.DEFAULT_WATER_MODEL) - @pytest.mark.parametrize('test_model', WATERMODELS) - def test_get_solvent_identifier_water(self, test_model): - def _assert_model(model): - assert mdpow.forcefields.get_solvent_identifier('water', model=model) is model - - _assert_model(test_model) - - @pytest.mark.parametrize('test_solvent', [model for model in SOLVENTMODELS if model != "water"]) - def test_get_solvent_identifier_solvents(self, test_solvent): - def _assert_model(solvent, model): - assert mdpow.forcefields.get_solvent_identifier(solvent, model=model) is solvent - - _assert_model(test_solvent, None) + @pytest.mark.parametrize("model", WATERMODELS) + def test_get_solvent_identifier_water(self, model): + assert mdpow.forcefields.get_solvent_identifier('water', model=model) is model - # make sure that model is ignored - _assert_model(test_solvent, "Jabberwock model") + @pytest.mark.parametrize('solvent', + [model for model in SOLVENTMODELS if model != "water"]) + @pytest.mark.parametrize('model', [None, "Jabberwock model"]) + def test_get_solvent_identifier_solvents(self, solvent, model): + # The model="Jabberwock model" checks that "model" is properly ignored. + assert mdpow.forcefields.get_solvent_identifier(solvent, model=model) is solvent @staticmethod def test_get_solvent_identifier_None(): diff --git a/mdpow/tests/test_solvation.py b/mdpow/tests/test_solvation.py index 4acedc01..c97d4ac5 100644 --- a/mdpow/tests/test_solvation.py +++ b/mdpow/tests/test_solvation.py @@ -3,6 +3,7 @@ import mdpow.equil from gromacs.utilities import in_dir +import gromacs import pytest @@ -51,8 +52,11 @@ def test_solvation_octanol(setup, ff): def test_solvation_cyclohexane(setup): solvation(setup, "cyclohexane") - -@pytest.mark.xfail("gromacs.release().startswith('2019')") +@pytest.mark.xfail(gromacs.release.startswith('4') + or gromacs.release.startswith('5') + or gromacs.release.startswith('2016'), + reason="GROMACS < 2018 cannot easily work with mixed solvents " + "(see issue #111)") @pytest.mark.parametrize("ff", ['OPLS-AA', 'CHARMM', 'AMBER']) def test_solvation_wetoctanol(setup, ff): solvation(setup, "wetoctanol", ff) From c8362085be7bd3ef616d9d35d9a058777b6df1e4 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Fri, 23 Jul 2021 12:56:23 -0700 Subject: [PATCH 10/56] Update project information --- CHANGES | 3 ++- README.rst | 2 +- doc/sphinx/source/conf.py | 2 +- doc/sphinx/source/index.txt | 6 +++--- mdpow/__init__.py | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 311eeeb3..033d5e22 100644 --- a/CHANGES +++ b/CHANGES @@ -6,9 +6,10 @@ Add summary of changes for each release. Use ISO dates. Reference GitHub issues numbers and PR numbers. 2021-xx-xx 0.7.0 -orbeckst, VOD555 +orbeckst, VOD555, ALescoulie * renamed package to MDPOW +* tested with GROMACS 4.6.5, 2018.6, 2020.6, 2021.1 (PR #159, #164) * removed all generated docs from package * config parser MERGES user runinput.yml with the package defaults (#8) diff --git a/README.rst b/README.rst index e026cd7c..56242305 100644 --- a/README.rst +++ b/README.rst @@ -47,7 +47,7 @@ See `INSTALL`_ for detailed instructions. Note that **only Python 2.7** is supported. You will also need `Gromacs`_ (currently tested with versions 4.6.5, -2018, 2019, 2021 but 2016 and 2020 should also work). +2018, 2020, 2021 but 2016 and 2019 should also work). Development version diff --git a/doc/sphinx/source/conf.py b/doc/sphinx/source/conf.py index 269689a5..861d6bdc 100644 --- a/doc/sphinx/source/conf.py +++ b/doc/sphinx/source/conf.py @@ -40,7 +40,7 @@ # General information about the project. project = u'MDPOW' -copyright = u'2010–2021, Shujie Fan, Ian Kenney, Bogdan Iorga, and Oliver Beckstein' +copyright = u'2010–2021, Shujie Fan, Ian Kenney, Alia Lescoulie, Bogdan Iorga, and Oliver Beckstein' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/doc/sphinx/source/index.txt b/doc/sphinx/source/index.txt index 5ff755ff..80f32e21 100644 --- a/doc/sphinx/source/index.txt +++ b/doc/sphinx/source/index.txt @@ -42,10 +42,10 @@ software package [#GromacsWrapperFramework]_. MDPOW is tested with * Gromacs 4.6.5 * Gromacs 2018.6 -* Gromacs 2019.1 -* Gromacs 2021.1 +* Gromacs 2020.6 +* Gromacs 2021.1 -but versions 5.x, 2016.x, and 2020.x should also work. +but versions 5.x, 2016.x, and 2019.x should also work. It should be possible to use any of these Gromacs versions without further adjustments, thanks to the underlying GromacsWrapper library [#GromacsWrapperFramework]_. diff --git a/mdpow/__init__.py b/mdpow/__init__.py index 3ab8d25f..05df3964 100644 --- a/mdpow/__init__.py +++ b/mdpow/__init__.py @@ -26,7 +26,7 @@ def create_logger(logfile="mdpow.log"): def log_banner(): """Log program name and licence at INFO level.""" logger.info("MDPOW %s starting.", get_version()) - logger.info("Copyright (c) 2010-2021 Shujie Fan, Ian Kenney, Bogdan Iorga, and Oliver Beckstein") + logger.info("Copyright (c) 2010-2021 Shujie Fan, Ian Kenney, Alia Lescoulie, Bogdan Iorga, and Oliver Beckstein") logger.info("Released under the GNU Public Licence, version 3.") logger.info("For bug reports and help: https://github.com/Becksteinlab/MDPOW/issues") From 1cd38111f25de7f712b900621e142ccf736dfbc4 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Fri, 23 Jul 2021 12:56:47 -0700 Subject: [PATCH 11/56] Update workflow to current --- .github/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 382fb1ad..1dd9700d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -26,7 +26,7 @@ jobs: #python-version: [2.7] python-version: [2.7, 3.8] #python-version: [2.7, 3.6, 3.7, 3.8, 3.9] - gromacs-version: ["4.6.5", "2018.6", "2019.1", "2021.1"] + gromacs-version: ["4.6.5", "2018.6", "2020.6", "2021.1"] # only test one GROMACS version on macOS to keep the testing # matrix manageable exclude: @@ -35,7 +35,7 @@ jobs: - os: macOS-latest gromacs-version: "2018.6" - os: macOS-latest - gromacs-version: "2019.1" + gromacs-version: "2020.6" env: MPLBACKEND: agg From c6b8c9d9326ccbf517f794b515b9687ec0b6b51d Mon Sep 17 00:00:00 2001 From: alescoulie Date: Fri, 23 Jul 2021 12:58:39 -0700 Subject: [PATCH 12/56] Update scripts and fix outdated script exception syntax --- scripts/mdpow-pcw | 122 ++++++++++++++++++---------------- scripts/mdpow-solvationenergy | 31 ++++----- 2 files changed, 82 insertions(+), 71 deletions(-) diff --git a/scripts/mdpow-pcw b/scripts/mdpow-pcw index 855a9002..c0af1f41 100755 --- a/scripts/mdpow-pcw +++ b/scripts/mdpow-pcw @@ -1,5 +1,5 @@ #!/usr/bin/env python -"""%prog [options] DIRECTORY [DIRECTORY ...] +"""%(prog)s [options] DIRECTORY [DIRECTORY ...] Run the free energy analysis for water and cyclohexane in /FEP and return the cyclohexane-water partition coefficient log P_cw. @@ -183,61 +183,71 @@ def realpath_or_none(fn): if __name__ == "__main__": import sys import os.path - from optparse import OptionParser - - parser = OptionParser(usage=__doc__) - parser.add_option('-p', '--plotfile', dest="plotfile", - help="plot dV/dlambda to FILE; use png or pdf suffix to " - "determine the file type. 'auto' generates a pdf file " - "DIRECTORY/dVdl_MOLID_pcw.pdf and 'None' disables it [%default]" - "The plot function is only available for mdpow estimator," - "and is disabled when using alchemlyb estimators.", - metavar="FILE") - parser.add_option('-o', '--outfile', dest="outfile", - help="append one-line results summary to FILE [%default]", - metavar="FILE") - parser.add_option('-e', '--energies', dest="energyfile", - help="append solvation free energies to FILE [%default]", - metavar="FILE") - parser.add_option('--estimator', dest="estimator", default='alchemlyb', - help="choose the estimator to be used, alchemlyb or mdpow estimators") - parser.add_option('--method', dest="method", default='MBAR', - help="choose the method to calculate free energy",) - parser.add_option('--force', dest='force', - action="store_true", - help="force rereading all data [%default]") - parser.add_option('--noSI', dest='SI', - action="store_false", - help="Disable statistical inefficiency analysis." - "Statitical inefficiency analysis is performed by default when using" - "alchemlyb estimators and is disabled when using mdpow estimator.") - parser.add_option('-s', '--stride', dest="stride", type='int', - help="Use every N-th datapoint from the original dV/dlambda data. " - "[%default]", - metavar="N") - parser.add_option('--start', dest="start", type='int', - help="Start point for the data used from the original dV/dlambda data. ") - parser.add_option('--stop', dest="stop", type='int', - help="Stop point for the data used from the original dV/dlambda data.",) - parser.add_option('--ignore-corrupted', dest="permissive", - action="store_true", - help="skip lines in the md.xvg files that cannot be parsed. " - "WARNING: Other lines in the file might have been corrupted in " - "such a way that they appear correct but are in fact wrong. " - "WRONG RESULTS CAN OCCUR! USE AT YOUR OWN RISK [%default]") - parser.set_defaults(plotfile="none", - outfile="pcw.txt", energyfile="energies.txt", - force=False, stride=1, permissive=False, SI=True) - opts,args = parser.parse_args() - - if len(args) == 0: - logger.fatal("A directory is required. See --help.") - sys.exit(1) - elif len(args) > 1 and not opts.plotfile.lower() in ('none', 'auto'): + import argparse + + parser = argparse.ArgumentParser(usage=__doc__, prog='mdpow-pcw', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument("directory", nargs='+', + help="directory or directories which contain all the files " + "resulting from running mdpow.fep.Ghyd.setup() and mdpow.fep.Gcylo.setup()" + "and the results of the MD FEP simulations.",) + parser.add_argument('--plotfile', dest="plotfile", default='none', + help="plot dV/dlambda to FILE; use png or pdf suffix to " + "determine the file type. 'auto' generates a pdf file " + "DIRECTORY/dVdl_MOLID_pcw.pdf and 'none' disables it " + "The plot function is only available for mdpow estimator," + "and is disabled when using alchemlyb estimators.", + metavar="FILE") + parser.add_argument('-o', '--outfile', dest="outfile", default="pcw.txt", + help="append one-line results summary to FILE ", + metavar="FILE") + parser.add_argument('-e', '--energies', dest="energyfile", default="energies.txt", + help="append solvation free energies to FILE ", + metavar="FILE") + parser.add_argument('--estimator', dest="estimator", default='alchemlyb', + choices=['mdpow', 'alchemlyb'], + help="choose the estimator to be used, alchemlyb or mdpow estimators") + parser.add_argument('--method', dest="method", default='MBAR', + choices=['TI', 'MBAR', 'BAR'], + help="choose the method to calculate free energy ",) + parser.add_argument('--force', dest='force', default=False, + action="store_true", + help="force rereading all data ") + SIs = parser.add_mutually_exclusive_group() + SIs.add_argument('--SI', dest='SI', + action="store_true", default=True, + help="enable statistical inefficiency (SI) analysis. " + "Statistical inefficiency analysis is performed by default when using" + "alchemlyb estimators and is always disabled when using mdpow estimator.") + SIs.add_argument('--no-SI', dest='noSI', + action="store_true", default=False, + help="disable statistical inefficiency analysis. " + "Statistical inefficiency analysis is performed by default when using" + "alchemlyb estimators and is disabled when using mdpow estimator. ") + parser.add_argument('-s', '--stride', dest="stride", type=int, default=1, + help="use every N-th datapoint from the original dV/dlambda data. ", + metavar="N") + parser.add_argument('--start', dest="start", type=int, default=0, + help="start point for the data used from the original dV/dlambda data. ") + parser.add_argument('--stop', dest="stop", type=int, default=None, + help="stop point for the data used from the original dV/dlambda data. ",) + parser.add_argument('--ignore-corrupted', dest="permissive", + action="store_true", default=False, + help="skip lines in the md.xvg files that cannot be parsed. " + "WARNING: Other lines in the file might have been corrupted in " + "such a way that they appear correct but are in fact wrong. " + "WRONG RESULTS CAN OCCUR! USE AT YOUR OWN RISK ") + opts = parser.parse_args() + + if len(opts.directory) > 1 and not opts.plotfile.lower() in ('none', 'auto'): logger.fatal("Can only use --plotfile=None or --plotfile=auto with multiple directories.") sys.exit(1) - for directory in args: + if opts.estimator == 'mdpow' and opts.SI and not opts.noSI: + logger.fatal("Statistical inefficiency analysis is only available for estimator 'alchemlyb'.") + sys.exit(1) + + for directory in opts.directory: if not os.path.exists(directory): logger.warn("Directory %r not found, skipping...", directory) continue @@ -251,10 +261,10 @@ if __name__ == "__main__": energyfile=realpath_or_none(opts.energyfile), force=opts.force, stride=opts.stride, start=opts.start, stop=opts.stop, estimator=opts.estimator, - method=opts.method, permissive=opts.permissive, SI=opts.SI) - except (OSError, IOError), err: + method=opts.method, permissive=opts.permissive, SI=opts.SI and not opts.noSI) + except (OSError, IOError) as err: logger.error("Running analysis in directory %r failed: %s", directory, str(err)) - except Exception, err: + except Exception as err: logger.fatal("Running analysis in directory %r failed", directory) logger.exception("Catastrophic problem occurred, see the stack trace for hints.") raise diff --git a/scripts/mdpow-solvationenergy b/scripts/mdpow-solvationenergy index 33bb334f..e93dac19 100755 --- a/scripts/mdpow-solvationenergy +++ b/scripts/mdpow-solvationenergy @@ -224,7 +224,7 @@ if __name__ == "__main__": parser.add_argument('--plotfile', dest="plotfile", default='none', help="plot dV/dlambda to FILE; use png or pdf suffix to " "determine the file type. 'auto' generates a pdf file " - "DIRECTORY/dVdl__.pdf and 'None' disables it " + "DIRECTORY/dVdl__.pdf and 'none' disables it " "The plot function is only available for mdpow estimator," "and is disabled when using alchemlyb estimators.", metavar="FILE") @@ -246,16 +246,17 @@ if __name__ == "__main__": parser.add_argument('--force', dest='force', default=False, action="store_true", help="force rereading all data ") - parser.add_argument('--SI', dest='SI', - action="store_true", default=True, - help="enable statistical inefficiency (SI) analysis. " - "Statistical inefficiency analysis is performed by default when using" - "alchemlyb estimators and is always disabled when using mdpow estimator.") - parser.add_argument('--no-SI', dest='SI', - action="store_false", default=True, - help="disable statistical inefficiency analysis. " - "Statistical inefficiency analysis is performed by default when using" - "alchemlyb estimators and is disabled when using mdpow estimator. ") + SIs = parser.add_mutually_exclusive_group() + SIs.add_argument('--SI', dest='SI', + action="store_true", default=True, + help="enable statistical inefficiency (SI) analysis. " + "Statistical inefficiency analysis is performed by default when using" + "alchemlyb estimators and is always disabled when using mdpow estimator.") + SIs.add_argument('--no-SI', dest='noSI', + action="store_true", default=False, + help="disable statistical inefficiency analysis. " + "Statistical inefficiency analysis is performed by default when using" + "alchemlyb estimators and is disabled when using mdpow estimator. ") parser.add_argument('-s', '--stride', dest="stride", type=int, default=1, help="use every N-th datapoint from the original dV/dlambda data. ", metavar="N") @@ -275,7 +276,7 @@ if __name__ == "__main__": logger.fatal("Can only use --plotfile=None or --plotfile=auto with multiple directories.") sys.exit(1) - if opts.estimator == 'mdpow' and opts.SI: + if opts.estimator == 'mdpow' and opts.SI and not opts.noSI: logger.fatal("Statistical inefficiency analysis is only available for estimator 'alchemlyb'.") sys.exit(1) @@ -292,10 +293,10 @@ if __name__ == "__main__": energyfile=realpath_or_none(opts.energyfile), force=opts.force, stride=opts.stride, start=opts.start, stop=opts.stop, estimator=opts.estimator, - method=opts.method, permissive=opts.permissive, SI=opts.SI) - except (OSError, IOError), err: + method=opts.method, permissive=opts.permissive, SI=opts.SI and not opts.noSI) + except (OSError, IOError) as err: logger.error("Running analysis in directory %r failed: %s", directory, str(err)) - except Exception, err: + except Exception as err: logger.fatal("Running analysis in directory %r failed", directory) logger.exception("Catastrophic problem occurred, see the stack trace for hints.") raise From 3ed5c7493e5c99cdca9c4bee94cd2587b48f2913 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Fri, 23 Jul 2021 14:33:46 -0700 Subject: [PATCH 13/56] Update tests for python compatibility --- mdpow/tests/test_analysis.py | 15 ++++++++++----- mdpow/tests/test_analysis_alchemlyb.py | 16 +++++++++++----- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/mdpow/tests/test_analysis.py b/mdpow/tests/test_analysis.py index 5d6fafd6..9caab7ff 100644 --- a/mdpow/tests/test_analysis.py +++ b/mdpow/tests/test_analysis.py @@ -1,4 +1,6 @@ import os.path +import sys + import pytest import py.path @@ -63,11 +65,14 @@ def fep_benzene_directory(tmpdir_factory): class TestAnalyze(object): def get_Gsolv(self, pth): gsolv = pth.join("FEP", "water", "Gsolv.fep") - with open(gsolv, 'rb') as f: - G = pickle.load(f, encoding='latin1') - # patch paths - G.basedir = pth.strpath - G.filename = gsolv.strpath + if sys.version_info.major == 2: + G = pickle.load(gsolv.open()) + elif sys.version_info.major == 3: + with open(gsolv, 'rb') as f: + G = pickle.load(f, encoding='latin1') + # patch paths + G.basedir = pth.strpath + G.filename = gsolv.strpath return G @staticmethod diff --git a/mdpow/tests/test_analysis_alchemlyb.py b/mdpow/tests/test_analysis_alchemlyb.py index 1ac6deb5..1d850156 100644 --- a/mdpow/tests/test_analysis_alchemlyb.py +++ b/mdpow/tests/test_analysis_alchemlyb.py @@ -1,4 +1,6 @@ import os.path +import sys + import pytest import py.path @@ -62,11 +64,15 @@ def fep_benzene_directory(tmpdir_factory): class TestAnalyze(object): def get_Gsolv(self, pth): gsolv = pth.join("FEP", "water", "Gsolv.fep") - with open(gsolv, 'rb') as f: - G = pickle.load(f, encoding='latin1') - # patch paths - G.basedir = pth.strpath - G.filename = gsolv.strpath + if sys.version_info.major == 3: + with open(gsolv, 'rb') as f: + G = pickle.load(f, encoding='latin1') + # patch paths + elif sys.version_info.major == 2: + G = pickle.load(gsolv.open()) + G.basedir = pth.strpath + G.filename = gsolv.strpath + return G @pytest.mark.parametrize('method, Gibbs, coulomb, vdw', [ From 6779c73c4dcd842d9675174f14a4a2b4b52674ee Mon Sep 17 00:00:00 2001 From: alescoulie Date: Fri, 23 Jul 2021 20:59:56 -0700 Subject: [PATCH 14/56] Undoing path changes in tests --- mdpow/tests/test_equilibration_script.py | 2 +- mdpow/tests/test_fep_script.py | 7 ++++--- mdpow/tests/test_runinput.py | 3 ++- mdpow/tests/test_solvation.py | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/mdpow/tests/test_equilibration_script.py b/mdpow/tests/test_equilibration_script.py index 8670513f..a031f206 100644 --- a/mdpow/tests/test_equilibration_script.py +++ b/mdpow/tests/test_equilibration_script.py @@ -13,7 +13,7 @@ def setup(self): self.tmpdir = td.TempDir() self.old_path = os.getcwd() self.resources = os.path.join( - self.old_path, 'testing_resources') + self.old_path, 'mdpow', 'tests', 'testing_resources') m = pybol.Manifest(os.path.join(self.resources, 'manifest.yml')) m.assemble('base', self.tmpdir.name) diff --git a/mdpow/tests/test_fep_script.py b/mdpow/tests/test_fep_script.py index 26207163..7d756193 100644 --- a/mdpow/tests/test_fep_script.py +++ b/mdpow/tests/test_fep_script.py @@ -11,9 +11,10 @@ class TestFEPScript(object): def setup(self): self.tmpdir = td.TempDir() self.old_path = os.getcwd() - self.resources = os.path.join(self.old_path, 'testing_resources') - self.m = pybol.Manifest(os.path.join(self.resources, 'manifest.yml')) - self.m.assemble('md_npt', self.tmpdir.name) + self.resources = os.path.join( + self.old_path, 'mdpow', 'tests', 'testing_resources') + self.m = pybol.Manifest(os.path.join(self.resources,'manifest.yml')) + self.m.assemble('md_npt',self.tmpdir.name) S = Simulation(filename=os.path.join( self.tmpdir.name, 'benzene', 'water.simulation')) diff --git a/mdpow/tests/test_runinput.py b/mdpow/tests/test_runinput.py index a9dca87d..3bb2f1dd 100644 --- a/mdpow/tests/test_runinput.py +++ b/mdpow/tests/test_runinput.py @@ -85,8 +85,9 @@ class TestAlteredConfig(object): def setup(self): # load default bundled configuration + self.old_path = os.getcwd() self.cfg = mdpow.config.get_configuration( - os.path.join('mdpow', 'tests', 'testing_resources', + os.path.join(self.old_path, 'mdpow', 'tests', 'testing_resources', 'test_configurations', 'altered_runinput.yml')) def _test_section(self,section): diff --git a/mdpow/tests/test_solvation.py b/mdpow/tests/test_solvation.py index c97d4ac5..e4d4da8f 100644 --- a/mdpow/tests/test_solvation.py +++ b/mdpow/tests/test_solvation.py @@ -20,10 +20,10 @@ @pytest.fixture def setup(tmpdir): - os.system('echo') newdir = tmpdir.mkdir('resources') old_path = os.getcwd() - resources = os.path.join(old_path, 'testing_resources') + resources = os.path.join( + old_path, 'mdpow', 'tests', 'testing_resources') files = ['benzene.pdb', 'benzene.itp', 'benzene_charmm.itp', 'benzene_amber.itp'] for f in files: From a9dc7fdf6bdff982ef1817a330bbddf6189a29d9 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Fri, 23 Jul 2021 21:01:13 -0700 Subject: [PATCH 15/56] Updating log for python 2 compatibility --- mdpow/config.py | 2 ++ mdpow/equil.py | 9 ++++----- mdpow/fep.py | 19 ++++++++++++------- mdpow/forcefields.py | 3 ++- mdpow/restart.py | 2 ++ mdpow/run.py | 2 ++ 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/mdpow/config.py b/mdpow/config.py index b96b24ac..a2cb237d 100644 --- a/mdpow/config.py +++ b/mdpow/config.py @@ -100,6 +100,8 @@ import logging logger = logging.getLogger("mdpow.config") +if not hasattr(logger, "warning"): + logger.warning = logger.warn # Reading of configuration files diff --git a/mdpow/equil.py b/mdpow/equil.py index 0982751f..ceff163c 100644 --- a/mdpow/equil.py +++ b/mdpow/equil.py @@ -50,6 +50,8 @@ import logging logger = logging.getLogger('mdpow.equil') +if not hasattr(logger, "warning"): + logger.warning = logger.warn # ITP <-- forcefields.get_solvent_model(id).itp # BOX <-- forcefields.get_solvent_model(id).coordinates @@ -228,7 +230,7 @@ def save(self, filename=None): else: self.filename = filename with open(filename, 'wb') as f: - pickle.dump(self, f) + pickle.dump(self, f, protocol=pickle.HIGHEST_PROTOCOL) logger.debug("Instance pickled to %(filename)r" % vars()) def load(self, filename=None): @@ -611,10 +613,7 @@ def get_last_checkpoint(self): """Returns the checkpoint of the most advanced step in the protocol. Relies on md.gro being present from previous simulation, assumes that checkpoint is then present. """ - if self._lastnotempty([self.files[name] for name in self.checkpoints]) is not None: - return self._lastnotempty([self.files[name] for name in self.checkpoints]).replace('.gro', '.cpt') - return None - + return self._lastnotempty([self.files[name] for name in self.checkpoints]).replace('.gro','.cpt') class WaterSimulation(Simulation): """Equilibrium MD of a solute in a box of water.""" diff --git a/mdpow/fep.py b/mdpow/fep.py index fcc0d94f..20b5dcca 100644 --- a/mdpow/fep.py +++ b/mdpow/fep.py @@ -151,9 +151,15 @@ from gromacs.utilities import asiterable, AttributeDict, in_dir, openany from numkit.observables import QuantityWithError from glob import glob +if sys.version_info.major == 2: + from ConfigParser import NoOptionError +elif sys.version_info.major == 3: + from configparser import NoOptionError import logging logger = logging.getLogger('mdpow.fep') +if not hasattr(logger, "warning"): + logger.warning = logger.warn from . import config from .restart import Journalled @@ -232,7 +238,6 @@ def mdp_dict(self): @staticmethod def load(cfg, section): """Initialize a :class:`FEPschedule` from the *section* in the configuration *cfg*""" - from configparser import NoOptionError keys = {} keys.update(FEPschedule.mdp_keywords) keys.update(FEPschedule.meta_keywords) @@ -481,7 +486,8 @@ def __init__(self, molecule=None, top=None, struct=None, method="BAR", **kwargs) self.mdp = kwargs.pop('mdp', config.get_template(self.mdp_default)) # schedules (deepcopy because we might modify) - self.schedules = copy.deepcopy(self.schedules_default) + # For some reason 2.7 tests failed with deepcopy in 2.7 so used merge_dict instead + self.schedules = config.merge_dicts(self.schedules_default, {}) schedules = kwargs.pop('schedules', {}) self.schedules.update(schedules) self.lambdas = { @@ -865,20 +871,19 @@ def contains_corrupted_xvgs(self): :attr:`Gsolv._corrupted` as dicts of dicts with the component as primary and the lambda as secondary key. """ - + from itertools import izip def _lencorrupted(xvg): try: return len(xvg.corrupted_lineno) except AttributeError: # backwards compatible (pre gw 0.1.10 are always ok) return 0 - except TypeError: # len(None): XVG.parse() has not been run yet - return 0 # ... so we cannot conclude that it does contain bad ones - + except TypeError: # len(None): XVG.parse() has not been run yet + return 0 # ... so we cannot conclude that it does contain bad ones corrupted = {} self._corrupted = {} # debugging ... for component, (lambdas, xvgs) in self.results.xvg.items(): corrupted[component] = numpy.any([(_lencorrupted(xvg) > 0) for xvg in xvgs]) - self._corrupted[component] = dict(((l, _lencorrupted(xvg)) for l, xvg in zip(lambdas, xvgs))) + self._corrupted[component] = dict(((l, _lencorrupted(xvg)) for l,xvg in izip(lambdas, xvgs))) return numpy.any([x for x in corrupted.values()]) def analyze(self, force=False, stride=None, autosave=True, ncorrel=25000): diff --git a/mdpow/forcefields.py b/mdpow/forcefields.py index c0a570b2..6a1bf79a 100644 --- a/mdpow/forcefields.py +++ b/mdpow/forcefields.py @@ -56,7 +56,8 @@ import logging logger = logging.getLogger("mdpow.forecefields") - +if not hasattr(logger, "warning"): + logger.warning = logger.warn #: Default force field. At the moment, only OPLS-AA is directly #: supported. diff --git a/mdpow/restart.py b/mdpow/restart.py index ca1fa1fc..b24d10ee 100644 --- a/mdpow/restart.py +++ b/mdpow/restart.py @@ -29,6 +29,8 @@ import logging logger = logging.getLogger('mdpow.checkpoint') +if not hasattr(logger, "warning"): + logger.warning = logger.warn def checkpoint(name, sim, filename): """Execute the :meth:`Journalled.save` method and log the event.""" diff --git a/mdpow/run.py b/mdpow/run.py index 2c3505cb..79779574 100644 --- a/mdpow/run.py +++ b/mdpow/run.py @@ -52,6 +52,8 @@ import logging logger = logging.getLogger('mdpow.run') +if not hasattr(logger, "warning"): + logger.warning = logger.warn def setupMD(S, protocol, cfg): From 05e4977a9e154167348812d9cddedeae46e19858 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Fri, 23 Jul 2021 21:08:11 -0700 Subject: [PATCH 16/56] Add argparse to mdpow-pow, correcting github account on AUTHORS --- AUTHORS | 5 ++ scripts/mdpow-pow | 124 +++++++++++++++++++++++++--------------------- 2 files changed, 72 insertions(+), 57 deletions(-) diff --git a/AUTHORS b/AUTHORS index 3015461d..22fe321c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -28,3 +28,8 @@ their first commit. GitHub handle is optional. ---- - Shujie Fan (VOD555) + +2021 +---- + +- Alia Lescoulie (ALescoulie) \ No newline at end of file diff --git a/scripts/mdpow-pow b/scripts/mdpow-pow index 3fd198b3..1bd95223 100755 --- a/scripts/mdpow-pow +++ b/scripts/mdpow-pow @@ -1,11 +1,11 @@ #!/usr/bin/env python -"""%prog [options] DIRECTORY [DIRECTORY ...] +"""%(prog)s [options] DIRECTORY [DIRECTORY ...] Run the free energy analysis for water and octanol in /FEP and return the octanol-water partition coefficient log P_ow. DIRECTORY should contain all the files resulting from running -``mdpow.fep.Goct.setup()`` and ``mdpow.fep.Goct.setup()`` and the results of +``mdpow.fep.Ghyd.setup()`` and ``mdpow.fep.Goct.setup()`` and the results of the MD FEP simulations. It relies on the canonical naming scheme (basically: just use the defaults as in the tutorial). @@ -183,61 +183,71 @@ def realpath_or_none(fn): if __name__ == "__main__": import sys import os.path - from optparse import OptionParser - - parser = OptionParser(usage=__doc__) - parser.add_option('-p', '--plotfile', dest="plotfile", - help="plot dV/dlambda to FILE; use png or pdf suffix to " - "determine the file type. 'auto' generates a pdf file " - "DIRECTORY/dVdl_MOLID_pow.pdf and 'None' disables it [%default]" - "The plot function is only available for mdpow estimator," - "and is disabled when using alchemlyb estimators.", - metavar="FILE") - parser.add_option('-o', '--outfile', dest="outfile", - help="append one-line results summary to FILE [%default]", - metavar="FILE") - parser.add_option('-e', '--energies', dest="energyfile", - help="append solvation free energies to FILE [%default]", - metavar="FILE") - parser.add_option('--estimator', dest="estimator", default='alchemlyb', - help="choose the estimator to be used, alchemlyb or mdpow estimators") - parser.add_option('--method', dest="method", default='MBAR', - help="choose the method to calculate free energy",) - parser.add_option('--force', dest='force', - action="store_true", - help="force rereading all data [%default]") - parser.add_option('--noSI', dest='SI', - action="store_false", - help="Disable statistical inefficiency analysis" - "Statistical inefficiency analysis is performed by default when using" - "alchemlyb estimators and is disabled when using mdpow estimator.") - parser.add_option('-s', '--stride', dest="stride", type='int', - help="Use every N-th datapoint from the original dV/dlambda data. " - "[%default]", - metavar="N") - parser.add_option('--start', dest="start", type='int', - help="Start point for the data used from the original dV/dlambda data. ") - parser.add_option('--stop', dest="stop", type='int', - help="Stop point for the data used from the original dV/dlambda data.",) - parser.add_option('--ignore-corrupted', dest="permissive", - action="store_true", - help="skip lines in the md.xvg files that cannot be parsed. " - "WARNING: Other lines in the file might have been corrupted in " - "such a way that they appear correct but are in fact wrong. " - "WRONG RESULTS CAN OCCUR! USE AT YOUR OWN RISK [%default]") - parser.set_defaults(plotfile="none", - outfile="pow.txt", energyfile="energies.txt", - force=False, stride=1, permissive=False, SI=True) - opts,args = parser.parse_args() - - if len(args) == 0: - logger.fatal("A directory is required. See --help.") - sys.exit(1) - elif len(args) > 1 and not opts.plotfile.lower() in ('none', 'auto'): + import argparse + + parser = argparse.ArgumentParser(usage=__doc__, prog='mdpow-pow', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument("directory", nargs='+', + help="directory or directories which contain all the files " + "resulting from running mdpow.fep.Ghyd.setup() and mdpow.fep.Goct.setup()" + "and the results of the MD FEP simulations.",) + parser.add_argument('--plotfile', dest="plotfile", default='none', + help="plot dV/dlambda to FILE; use png or pdf suffix to " + "determine the file type. 'auto' generates a pdf file " + "DIRECTORY/dVdl_MOLID_pcw.pdf and 'none' disables it " + "The plot function is only available for mdpow estimator," + "and is disabled when using alchemlyb estimators.", + metavar="FILE") + parser.add_argument('-o', '--outfile', dest="outfile", default="pow.txt", + help="append one-line results summary to FILE ", + metavar="FILE") + parser.add_argument('-e', '--energies', dest="energyfile", default="energies.txt", + help="append solvation free energies to FILE ", + metavar="FILE") + parser.add_argument('--estimator', dest="estimator", default='alchemlyb', + choices=['mdpow', 'alchemlyb'], + help="choose the estimator to be used, alchemlyb or mdpow estimators") + parser.add_argument('--method', dest="method", default='MBAR', + choices=['TI', 'MBAR', 'BAR'], + help="choose the method to calculate free energy ",) + parser.add_argument('--force', dest='force', default=False, + action="store_true", + help="force rereading all data ") + SIs = parser.add_mutually_exclusive_group() + SIs.add_argument('--SI', dest='SI', + action="store_true", default=True, + help="enable statistical inefficiency (SI) analysis. " + "Statistical inefficiency analysis is performed by default when using" + "alchemlyb estimators and is always disabled when using mdpow estimator.") + SIs.add_argument('--no-SI', dest='noSI', + action="store_true", default=False, + help="disable statistical inefficiency analysis. " + "Statistical inefficiency analysis is performed by default when using" + "alchemlyb estimators and is disabled when using mdpow estimator. ") + parser.add_argument('-s', '--stride', dest="stride", type=int, default=1, + help="use every N-th datapoint from the original dV/dlambda data. ", + metavar="N") + parser.add_argument('--start', dest="start", type=int, default=0, + help="start point for the data used from the original dV/dlambda data. ") + parser.add_argument('--stop', dest="stop", type=int, default=None, + help="stop point for the data used from the original dV/dlambda data. ",) + parser.add_argument('--ignore-corrupted', dest="permissive", + action="store_true", default=False, + help="skip lines in the md.xvg files that cannot be parsed. " + "WARNING: Other lines in the file might have been corrupted in " + "such a way that they appear correct but are in fact wrong. " + "WRONG RESULTS CAN OCCUR! USE AT YOUR OWN RISK ") + opts = parser.parse_args() + + if len(opts.directory) > 1 and not opts.plotfile.lower() in ('none', 'auto'): logger.fatal("Can only use --plotfile=None or --plotfile=auto with multiple directories.") sys.exit(1) - for directory in args: + if opts.estimator == 'mdpow' and opts.SI and not opts.noSI: + logger.fatal("Statistical inefficiency analysis is only available for estimator 'alchemlyb'.") + sys.exit(1) + + for directory in opts.directory: if not os.path.exists(directory): logger.warn("Directory %r not found, skipping...", directory) continue @@ -251,10 +261,10 @@ if __name__ == "__main__": energyfile=realpath_or_none(opts.energyfile), force=opts.force, stride=opts.stride, start=opts.start, stop=opts.stop, estimator=opts.estimator, - method=opts.method, permissive=opts.permissive, SI=opts.SI) - except (OSError, IOError), err: + method=opts.method, permissive=opts.permissive, SI=opts.SI and not opts.noSI) + except (OSError, IOError) as err: logger.error("Running analysis in directory %r failed: %s", directory, str(err)) - except Exception, err: + except Exception as err: logger.fatal("Running analysis in directory %r failed", directory) logger.exception("Catastrophic problem occurred, see the stack trace for hints.") raise From fc9071729d29b8373fbf26bbdff336b70ed24237 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Fri, 23 Jul 2021 21:34:51 -0700 Subject: [PATCH 17/56] Adding comment to test_analysis.py --- mdpow/tests/test_analysis.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mdpow/tests/test_analysis.py b/mdpow/tests/test_analysis.py index 9caab7ff..7d4a690b 100644 --- a/mdpow/tests/test_analysis.py +++ b/mdpow/tests/test_analysis.py @@ -68,6 +68,7 @@ def get_Gsolv(self, pth): if sys.version_info.major == 2: G = pickle.load(gsolv.open()) elif sys.version_info.major == 3: + # Needed to read old pickle files with open(gsolv, 'rb') as f: G = pickle.load(f, encoding='latin1') # patch paths From 1e023df989061647fcb851269812d81f7a301989 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Fri, 23 Jul 2021 21:59:07 -0700 Subject: [PATCH 18/56] Replacing izip import with six.moves.zip --- mdpow/fep.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mdpow/fep.py b/mdpow/fep.py index 20b5dcca..c22cd953 100644 --- a/mdpow/fep.py +++ b/mdpow/fep.py @@ -871,7 +871,7 @@ def contains_corrupted_xvgs(self): :attr:`Gsolv._corrupted` as dicts of dicts with the component as primary and the lambda as secondary key. """ - from itertools import izip + from six.moves import zip def _lencorrupted(xvg): try: return len(xvg.corrupted_lineno) @@ -883,7 +883,7 @@ def _lencorrupted(xvg): self._corrupted = {} # debugging ... for component, (lambdas, xvgs) in self.results.xvg.items(): corrupted[component] = numpy.any([(_lencorrupted(xvg) > 0) for xvg in xvgs]) - self._corrupted[component] = dict(((l, _lencorrupted(xvg)) for l,xvg in izip(lambdas, xvgs))) + self._corrupted[component] = dict(((l, _lencorrupted(xvg)) for l,xvg in zip(lambdas, xvgs))) return numpy.any([x for x in corrupted.values()]) def analyze(self, force=False, stride=None, autosave=True, ncorrel=25000): From 3ca7624021adf9d27758cfd395ddefbf4ce45594 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Sat, 24 Jul 2021 14:57:09 -0700 Subject: [PATCH 19/56] Adding __future__ imports to to tests --- mdpow/tests/test_Gsolv.py | 5 ++++- mdpow/tests/test_analysis.py | 4 ++++ mdpow/tests/test_analysis_alchemlyb.py | 4 ++++ mdpow/tests/test_emin.py | 5 ++++- mdpow/tests/test_equilibration_script.py | 5 ++++- mdpow/tests/test_fep.py | 4 ++++ mdpow/tests/test_fep_script.py | 5 ++++- mdpow/tests/test_filelock.py | 4 ++++ mdpow/tests/test_forcefields.py | 4 ++++ mdpow/tests/test_runinput.py | 4 ++++ mdpow/tests/test_solvation.py | 4 ++++ 11 files changed, 44 insertions(+), 4 deletions(-) diff --git a/mdpow/tests/test_Gsolv.py b/mdpow/tests/test_Gsolv.py index 063067c8..6f60f722 100644 --- a/mdpow/tests/test_Gsolv.py +++ b/mdpow/tests/test_Gsolv.py @@ -1,4 +1,7 @@ -import tempdir as td +from __future__ import absolute_import + +from . import tempdir as td + import os import pybol import numpy as np diff --git a/mdpow/tests/test_analysis.py b/mdpow/tests/test_analysis.py index 7d4a690b..9efde3a5 100644 --- a/mdpow/tests/test_analysis.py +++ b/mdpow/tests/test_analysis.py @@ -1,3 +1,7 @@ +from __future__ import absolute_import + +from . import tempdir + import os.path import sys diff --git a/mdpow/tests/test_analysis_alchemlyb.py b/mdpow/tests/test_analysis_alchemlyb.py index 1d850156..88c7b032 100644 --- a/mdpow/tests/test_analysis_alchemlyb.py +++ b/mdpow/tests/test_analysis_alchemlyb.py @@ -1,3 +1,7 @@ +from __future__ import absolute_import + +from . import tempdir + import os.path import sys diff --git a/mdpow/tests/test_emin.py b/mdpow/tests/test_emin.py index b745cc91..f0b550a8 100644 --- a/mdpow/tests/test_emin.py +++ b/mdpow/tests/test_emin.py @@ -1,5 +1,8 @@ +from __future__ import absolute_import + +from . import tempdir as td + import mdpow.equil -import tempdir as td import os class TestEnergyMinimization(object): diff --git a/mdpow/tests/test_equilibration_script.py b/mdpow/tests/test_equilibration_script.py index a031f206..34be0d06 100644 --- a/mdpow/tests/test_equilibration_script.py +++ b/mdpow/tests/test_equilibration_script.py @@ -1,5 +1,8 @@ +from __future__ import absolute_import + +from . import tempdir as td + import os.path -import tempdir as td import pybol from gromacs.utilities import in_dir diff --git a/mdpow/tests/test_fep.py b/mdpow/tests/test_fep.py index 81ed75fc..29a35f2a 100644 --- a/mdpow/tests/test_fep.py +++ b/mdpow/tests/test_fep.py @@ -1,3 +1,7 @@ +from __future__ import absolute_import + +from . import tempdir + import numpy as np from numpy.testing import assert_array_almost_equal, assert_almost_equal from scipy import constants diff --git a/mdpow/tests/test_fep_script.py b/mdpow/tests/test_fep_script.py index 7d756193..338b96ac 100644 --- a/mdpow/tests/test_fep_script.py +++ b/mdpow/tests/test_fep_script.py @@ -1,4 +1,7 @@ -import tempdir as td +from __future__ import absolute_import + +from . import tempdir as td + import os import pybol from mdpow.equil import Simulation diff --git a/mdpow/tests/test_filelock.py b/mdpow/tests/test_filelock.py index 704d90eb..d5f37917 100644 --- a/mdpow/tests/test_filelock.py +++ b/mdpow/tests/test_filelock.py @@ -1,3 +1,7 @@ +from __future__ import absolute_import + +from . import tempdir + import os.path import pytest diff --git a/mdpow/tests/test_forcefields.py b/mdpow/tests/test_forcefields.py index db556f80..96f508ef 100644 --- a/mdpow/tests/test_forcefields.py +++ b/mdpow/tests/test_forcefields.py @@ -1,3 +1,7 @@ +from __future__ import absolute_import + +from . import tempdir + import os.path import pytest diff --git a/mdpow/tests/test_runinput.py b/mdpow/tests/test_runinput.py index 3bb2f1dd..8f88c868 100644 --- a/mdpow/tests/test_runinput.py +++ b/mdpow/tests/test_runinput.py @@ -1,3 +1,7 @@ +from __future__ import absolute_import + +from . import tempdir + import os.path import numpy as np diff --git a/mdpow/tests/test_solvation.py b/mdpow/tests/test_solvation.py index e4d4da8f..b2a3b6da 100644 --- a/mdpow/tests/test_solvation.py +++ b/mdpow/tests/test_solvation.py @@ -1,3 +1,7 @@ +from __future__ import absolute_import + +from . import tempdir + import os import shutil From 6238f1070b4a7c98045561ca273aa9853a8f9310 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Sat, 24 Jul 2021 15:07:54 -0700 Subject: [PATCH 20/56] Simplifying merge_dicts --- mdpow/config.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mdpow/config.py b/mdpow/config.py index a2cb237d..80982e62 100644 --- a/mdpow/config.py +++ b/mdpow/config.py @@ -121,8 +121,6 @@ def merge_dicts(user, default): if key in user: if isinstance(user[key], dict) and isinstance(default[key], dict): merge_dicts(user[key], default[key]) - elif user[key] == default[key]: - pass else: user[key] = default[key] return user From 4594b4e2160d5ea8bf1705b804507d39bad58cc5 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Sat, 24 Jul 2021 15:47:01 -0700 Subject: [PATCH 21/56] Simplifications for config.py --- mdpow/config.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/mdpow/config.py b/mdpow/config.py index 80982e62..48b016ec 100644 --- a/mdpow/config.py +++ b/mdpow/config.py @@ -95,8 +95,10 @@ import os, errno from pkg_resources import resource_filename, resource_listdir import yaml +import six import numpy as np +import gromacs.utilities import logging logger = logging.getLogger("mdpow.config") @@ -170,12 +172,11 @@ def get(self, section, option): and "none") """ - value = self.conf[section] - if isinstance(value, dict): - if value[option] != 'None': - return value[option] - else: - return None + try: + value = self.conf[section][option] + return value if value != "None" else None + except TypeError: + return None # TODO: # The YAML parser does automatic conversion: the following @@ -292,7 +293,8 @@ def get_template(t): :Raises: :exc:`ValueError` if no file can be located. """ - templates = [_get_template(s) for s in asiterable(t)] + # Not sure if this is the best way to get asiterables + templates = [_get_template(s) for s in gromacs.utilities.asiterable(t)] if len(templates) == 1: return templates[0] return templates @@ -315,7 +317,7 @@ def get_templates(t): :Raises: :exc:`ValueError` if no file can be located. """ - return [_get_template(s) for s in asiterable(t)] + return [_get_template(s) for s in gromacs.utilities.asiterable(t)] def _get_template(t): """Return a single template *t*.""" @@ -346,7 +348,7 @@ def _get_template(t): def iterable(obj): """Returns ``True`` if *obj* can be iterated over and is *not* a string.""" - if isinstance(obj, str): + if isinstance(obj, six.string_types): return False # avoid iterating over characters of a string if hasattr(obj, 'next'): From 51b37b09e4092661ea17a87cc52f1c5247575606 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Sat, 24 Jul 2021 16:18:31 -0700 Subject: [PATCH 22/56] Simplifications for equil.py --- mdpow/equil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mdpow/equil.py b/mdpow/equil.py index ceff163c..f6885f08 100644 --- a/mdpow/equil.py +++ b/mdpow/equil.py @@ -33,9 +33,9 @@ import os, errno import shutil +from six.moves import cPickle as pickle import MDAnalysis as mda -from six.moves import cPickle as pickle try: import gromacs.setup, gromacs.cbook From 4cd8192a21adbd1cff2dd2a0bbe195b3faff1065 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Sat, 24 Jul 2021 16:19:01 -0700 Subject: [PATCH 23/56] Simplifify __deepcopy__ in fep.py --- mdpow/fep.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/mdpow/fep.py b/mdpow/fep.py index c22cd953..400568e6 100644 --- a/mdpow/fep.py +++ b/mdpow/fep.py @@ -127,13 +127,13 @@ import copy from subprocess import call import warnings - import sys import numpy import pandas as pd import scipy.integrate +import six from scipy import constants import numkit.integration import numkit.timeseries @@ -151,6 +151,8 @@ from gromacs.utilities import asiterable, AttributeDict, in_dir, openany from numkit.observables import QuantityWithError from glob import glob + +# Patch due to slightly different library names if sys.version_info.major == 2: from ConfigParser import NoOptionError elif sys.version_info.major == 3: @@ -257,18 +259,10 @@ def getter(type, section, key): if getter(keytype, section, key) is not None) def __deepcopy__(self, memo): - if sys.version_info.major == 3: - from collections.abc import Iterable - x = FEPschedule() - for k, v in self.items(): - if isinstance(k, Iterable) and isinstance(v, Iterable): - x[k] = copy.deepcopy(v) - return x - elif sys.version_info == 2: - x = FEPschedule() - for k, v in self.iteritems(): - x[k] = copy.deepcopy(v) - return x + x = FEPschedule() + for k, v in six.iteritems(self): + x[k] = copy.deepcopy(v) + return x class Gsolv(Journalled): """Simulations to calculate and analyze the solvation free energy. From 19ee8cfd1fadc9247425fcdb11a3c675efecb625 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Sat, 24 Jul 2021 17:00:39 -0700 Subject: [PATCH 24/56] Raise assert_almost_equal threshold and add xfail --- mdpow/tests/test_analysis_alchemlyb.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/mdpow/tests/test_analysis_alchemlyb.py b/mdpow/tests/test_analysis_alchemlyb.py index 88c7b032..51fd6a4e 100644 --- a/mdpow/tests/test_analysis_alchemlyb.py +++ b/mdpow/tests/test_analysis_alchemlyb.py @@ -12,6 +12,7 @@ import pybol from numpy.testing import assert_array_almost_equal +import pandas from six.moves import cPickle as pickle @@ -93,6 +94,9 @@ def get_Gsolv(self, pth): (8.241836, 0.219235), (-1.448719, 0.421548)) ]) + + @pytest.mark.xfail(pandas.__version__.startswith("1.3.0"), + reason="bug in pandas 1.3.0 see alchemistry/alchemlyb#147") def test_estimator_alchemlyb(self, fep_benzene_directory, method, Gibbs, coulomb, vdw): G = self.get_Gsolv(fep_benzene_directory) @@ -111,12 +115,14 @@ def test_estimator_alchemlyb(self, fep_benzene_directory, method, err.strerror, err.filename)) DeltaA = G.results.DeltaA assert_array_almost_equal(DeltaA.Gibbs.astuple(), Gibbs, - decimal=6) + decimal=5) # with more recent versions of pandas/alchemlyb/numpy the original values are only reproduced to 5 decimals, see PR #166") assert_array_almost_equal(DeltaA.coulomb.astuple(), coulomb, - decimal=6) + decimal=5) # with more recent versions of pandas/alchemlyb/numpy the original values are only reproduced to 5 decimals, see PR #166") assert_array_almost_equal(DeltaA.vdw.astuple(), vdw, - decimal=6) + decimal=5) # with more recent versions of pandas/alchemlyb/numpy the original values are only reproduced to 5 decimals, see PR #166") + @pytest.mark.xfail(pandas.__version__.startswith("1.3.0"), + reason="bug in pandas 1.3.0 see alchemistry/alchemlyb#147") def test_SI(self, fep_benzene_directory): G = self.get_Gsolv(fep_benzene_directory) G.method = 'TI' @@ -136,6 +142,8 @@ def test_SI(self, fep_benzene_directory): assert_array_almost_equal(DeltaA.vdw.astuple(), (-4.846894, 2.110071), decimal=6) + @pytest.mark.xfail(pandas.__version__.startswith("1.3.0"), + reason="bug in pandas 1.3.0 see alchemistry/alchemlyb#147") def test_start_stop_stride(self, fep_benzene_directory): G = self.get_Gsolv(fep_benzene_directory) G.method = 'TI' From f94469a03f318d9727044b8b344d8694fe8ee361 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Sat, 24 Jul 2021 21:44:11 -0700 Subject: [PATCH 25/56] fixed docs: version variable --- doc/sphinx/source/index.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/sphinx/source/index.txt b/doc/sphinx/source/index.txt index 4e647657..f846fdde 100644 --- a/doc/sphinx/source/index.txt +++ b/doc/sphinx/source/index.txt @@ -99,9 +99,9 @@ also change (rarely) between MINOR releases. *MINOR* releases can introduce new functionality or deprecate old ones. The version information can be accessed from the attribute -:data:`gromacs.__version__`. +:data:`mdpow.__version__`. -.. autodata:: gromacs.__version__ +.. autodata:: mdpow.__version__ .. _semantic versioning: https://semver.org @@ -126,7 +126,7 @@ For current issues and open feature requests please look through the -.. Hide to use with alabaster +.. Hide to use with RTD theme .. Contents: .. toctree:: From 541876045a7a95147fdf2309efd02e6e61bb36ae Mon Sep 17 00:00:00 2001 From: alescoulie Date: Sun, 25 Jul 2021 13:47:53 -0700 Subject: [PATCH 26/56] Update docs --- CHANGES | 1 + INSTALL.rst | 19 ++++++++++++++----- README.rst | 5 ++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 68f2ba2b..8dfa1570 100644 --- a/CHANGES +++ b/CHANGES @@ -9,6 +9,7 @@ GitHub issues numbers and PR numbers. orbeckst, VOD555, ALescoulie * renamed package to MDPOW +* support python 3.7 -- 3.9 * tested with GROMACS 4.6.5, 2018.6, 2020.6, 2021.1 (PR #159, #164) * removed all generated docs from package * config parser MERGES user runinput.yml with the package defaults diff --git a/INSTALL.rst b/INSTALL.rst index aea04f13..0d04a3c6 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -2,10 +2,9 @@ Quick installation instructions for *MDPOW* ============================================= -**Only Python 2.7 is supported** (Python 3 is *not* supported, see -`#84 `_). Python 2.7 -is rock-stable and frozen but not officially supported anymore by the -Python developers. +MDPOW is compatible with Python 2.7 and 3.7+ and tested +on Ubuntu and Mac OS. Python 2.7 is rock-stable and frozen but +not officially supported anymore by the Python developers. We recommend that you install MDPOW in a virtual environment. @@ -30,7 +29,7 @@ GROMACS_. Conda environment with pre-requisites ------------------------------------- -We make a conda environment with the latest packages for Python 2.7 +To make a conda environment with the latest packages for Python 2.7 and name it *mdpow*; this installs the larger dependencies that are pre-requisites for MDPOW:: @@ -38,6 +37,16 @@ pre-requisites for MDPOW:: conda activate mdpow pip install gromacswrapper + +For Python 3.7 and up. + +*Note:* with Pandas version 1.3 there is an error with `Alchemlyb `_ +(see `issue #147 `_) which will be fixed in Alchemlyb 0.5.:: + + conda create -c conda-forge -n mdpow python=3.7 numpy scipy 'matplotlib' 'mdanalysis' 'mdanalysistests' pyyaml + conda activate mdpow + pip install gromacswrapper + Installation from source ------------------------ diff --git a/README.rst b/README.rst index 56242305..21f90abf 100644 --- a/README.rst +++ b/README.rst @@ -10,7 +10,7 @@ *MDPOW* is a python package that automates the calculation of solvation free energies via molecular dynamics (MD) simulations. In particular, it facilitates the computation of partition -coeffcients. Currently implemented: +coefficients. Currently implemented: - *water-octanol* partition coefficient (|P_ow|) - *water-cyclohexane* partition coefficient (|P_cw|) @@ -43,8 +43,7 @@ Documentation Installation ------------ -See `INSTALL`_ for detailed instructions. Note that -**only Python 2.7** is supported. +See `INSTALL`_ for detailed instructions. MDPOW currently supports Python 2.7 and Python 3.7 to 3.9. You will also need `Gromacs`_ (currently tested with versions 4.6.5, 2018, 2020, 2021 but 2016 and 2019 should also work). From 9d3803a183b27ad462bd65def4c1a1135393fcd3 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Sun, 25 Jul 2021 13:50:44 -0700 Subject: [PATCH 27/56] Add comments to test_analysis_alchemlyb.py --- mdpow/tests/test_analysis_alchemlyb.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mdpow/tests/test_analysis_alchemlyb.py b/mdpow/tests/test_analysis_alchemlyb.py index 51fd6a4e..43ae8d91 100644 --- a/mdpow/tests/test_analysis_alchemlyb.py +++ b/mdpow/tests/test_analysis_alchemlyb.py @@ -69,7 +69,8 @@ def fep_benzene_directory(tmpdir_factory): class TestAnalyze(object): def get_Gsolv(self, pth): gsolv = pth.join("FEP", "water", "Gsolv.fep") - if sys.version_info.major == 3: + # Needed to load old pickle files in python 3 + if sys.version_info.major >= 3: with open(gsolv, 'rb') as f: G = pickle.load(f, encoding='latin1') # patch paths From b6491d700fc694e8f1d436e89c6cfa9652c97d7a Mon Sep 17 00:00:00 2001 From: alescoulie Date: Sun, 25 Jul 2021 13:52:34 -0700 Subject: [PATCH 28/56] Update setup.py with python 3 versions --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index 42a8a193..d549c138 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,10 @@ 'Programming Language :: Python', 'Programming Language :: Python :: 2', "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Topic :: Scientific/Engineering :: Chemistry", "Topic :: Scientific/Engineering :: Physics", ], From a6a3dc2d5a3a4754727a0d5d707645133e09a827 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Sun, 25 Jul 2021 14:00:02 -0700 Subject: [PATCH 29/56] Reduce testing thresholds for test_analysis.py --- mdpow/tests/test_analysis.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mdpow/tests/test_analysis.py b/mdpow/tests/test_analysis.py index 9efde3a5..2854a41d 100644 --- a/mdpow/tests/test_analysis.py +++ b/mdpow/tests/test_analysis.py @@ -85,14 +85,13 @@ def assert_DeltaA(G): DeltaA = G.results.DeltaA assert_array_almost_equal(DeltaA.Gibbs.astuple(), (-3.7217472974883794, 2.3144288928034911), - decimal=6) + decimal=5) # with more recent versions of pandas/alchemlyb/numpy the original values are only reproduced to 5 decimals, see PR #166") assert_array_almost_equal(DeltaA.coulomb.astuple(), (8.3346255170099575, 0.73620918517131495), - decimal=6) + decimal=5) # with more recent versions of pandas/alchemlyb/numpy the original values are only reproduced to 5 decimals, see PR #166") assert_array_almost_equal(DeltaA.vdw.astuple(), (-4.6128782195215781, 2.1942144688960972), - decimal=6) - + decimal=5) # with more recent versions of pandas/alchemlyb/numpy the original values are only reproduced to 5 decimals, see PR #166") def test_convert_edr(self, fep_benzene_directory): G = self.get_Gsolv(fep_benzene_directory) From 1310b7d487f37b8a619063bda4fbe310961c1427 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Sun, 25 Jul 2021 14:22:01 -0700 Subject: [PATCH 30/56] Updating paths test_runinput.py --- mdpow/tests/test_runinput.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mdpow/tests/test_runinput.py b/mdpow/tests/test_runinput.py index 8f88c868..71ac646a 100644 --- a/mdpow/tests/test_runinput.py +++ b/mdpow/tests/test_runinput.py @@ -91,7 +91,7 @@ def setup(self): # load default bundled configuration self.old_path = os.getcwd() self.cfg = mdpow.config.get_configuration( - os.path.join(self.old_path, 'mdpow', 'tests', 'testing_resources', + os.path.join('mdpow', 'tests', 'testing_resources', 'test_configurations', 'altered_runinput.yml')) def _test_section(self,section): From ac58484836e65a3cd5ca84cc37baa9ca82c11224 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Sun, 25 Jul 2021 17:00:36 -0700 Subject: [PATCH 31/56] Updating test thresholds test_analysis_alchemlyb.py --- mdpow/tests/test_analysis_alchemlyb.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mdpow/tests/test_analysis_alchemlyb.py b/mdpow/tests/test_analysis_alchemlyb.py index 43ae8d91..2d8378ad 100644 --- a/mdpow/tests/test_analysis_alchemlyb.py +++ b/mdpow/tests/test_analysis_alchemlyb.py @@ -137,11 +137,11 @@ def test_SI(self, fep_benzene_directory): err.strerror, err.filename)) DeltaA = G.results.DeltaA assert_array_almost_equal(DeltaA.Gibbs.astuple(), (-2.908885, 2.175976), - decimal=6) + decimal=5) # with more recent versions of pandas/alchemlyb/numpy the original values are only reproduced to 5 decimals, see PR #166") assert_array_almost_equal(DeltaA.coulomb.astuple(), (7.755779, 0.531481), - decimal=6) + decimal=5) # with more recent versions of pandas/alchemlyb/numpy the original values are only reproduced to 5 decimals, see PR #166") assert_array_almost_equal(DeltaA.vdw.astuple(), (-4.846894, 2.110071), - decimal=6) + decimal=5) # with more recent versions of pandas/alchemlyb/numpy the original values are only reproduced to 5 decimals, see PR #166") @pytest.mark.xfail(pandas.__version__.startswith("1.3.0"), reason="bug in pandas 1.3.0 see alchemistry/alchemlyb#147") @@ -159,8 +159,8 @@ def test_start_stop_stride(self, fep_benzene_directory): err.strerror, err.filename)) DeltaA = G.results.DeltaA assert_array_almost_equal(DeltaA.Gibbs.astuple(), (-3.318109, 0.905128), - decimal=6) + decimal=5) # with more recent versions of pandas/alchemlyb/numpy the original values are only reproduced to 5 decimals, see PR #166") assert_array_almost_equal(DeltaA.coulomb.astuple(), (8.146806, 0.348866), - decimal=6) + decimal=5) # with more recent versions of pandas/alchemlyb/numpy the original values are only reproduced to 5 decimals, see PR #166") assert_array_almost_equal(DeltaA.vdw.astuple(), (-4.828696, 0.835195), - decimal=6) + decimal=5) # with more recent versions of pandas/alchemlyb/numpy the original values are only reproduced to 5 decimals, see PR #166") From d6699204abb12837ea2a9e823532f6cf393a1afe Mon Sep 17 00:00:00 2001 From: alescoulie Date: Tue, 27 Jul 2021 11:25:38 -0700 Subject: [PATCH 32/56] Add test for fep imports and cleanup test_runinput.py --- mdpow/tests/test_fep.py | 7 ++++++- mdpow/tests/test_runinput.py | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mdpow/tests/test_fep.py b/mdpow/tests/test_fep.py index 29a35f2a..78acd1fd 100644 --- a/mdpow/tests/test_fep.py +++ b/mdpow/tests/test_fep.py @@ -1,5 +1,10 @@ from __future__ import absolute_import +import sys +from six.moves import reload_module + +import pytest + from . import tempdir import numpy as np @@ -8,7 +13,7 @@ import mdpow import mdpow.fep - +import gromacs def test_molar_to_nm3(): assert_almost_equal(mdpow.fep.molar_to_nm3(1.5), 0.9033212684) diff --git a/mdpow/tests/test_runinput.py b/mdpow/tests/test_runinput.py index 71ac646a..7a8f0682 100644 --- a/mdpow/tests/test_runinput.py +++ b/mdpow/tests/test_runinput.py @@ -89,7 +89,6 @@ class TestAlteredConfig(object): def setup(self): # load default bundled configuration - self.old_path = os.getcwd() self.cfg = mdpow.config.get_configuration( os.path.join('mdpow', 'tests', 'testing_resources', 'test_configurations', 'altered_runinput.yml')) From f57e4d9f0898da23152b283b3825443df10a7914 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Tue, 27 Jul 2021 11:26:01 -0700 Subject: [PATCH 33/56] Cleanup fep.py imports --- mdpow/fep.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mdpow/fep.py b/mdpow/fep.py index 400568e6..8a33709c 100644 --- a/mdpow/fep.py +++ b/mdpow/fep.py @@ -153,9 +153,9 @@ from glob import glob # Patch due to slightly different library names -if sys.version_info.major == 2: +try: from ConfigParser import NoOptionError -elif sys.version_info.major == 3: +except ImportError: from configparser import NoOptionError import logging From 20b3a647dd6581ebaa925945ad9945db84be7c11 Mon Sep 17 00:00:00 2001 From: alescoulie Date: Tue, 27 Jul 2021 15:06:28 -0700 Subject: [PATCH 34/56] Remove unnecessary exceptions fep.py --- mdpow/fep.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/mdpow/fep.py b/mdpow/fep.py index 8a33709c..847ade3f 100644 --- a/mdpow/fep.py +++ b/mdpow/fep.py @@ -1395,19 +1395,14 @@ def p_transfer(G1, G2, **kwargs): logger.info("Estimator is %s.", estimator) logger.info("Free energy calculation method is %s.", G.method) - try: - G.results.DeltaA.Gibbs - G.logger_DeltaA0() - except (KeyError, AttributeError): # KeyError because results is a AttributeDict - logger.warning("Must analyze simulation because no hydration free energy data available...") - if estimator == 'mdpow': - G.analyze(**G_kwargs) - elif estimator == 'alchemlyb': - if G_kwargs['SI']: - logger.info("Statistical inefficiency analysis will be performed.") - else: - logger.info("Statistical inefficiency analysis won't be performed.") - G.analyze_alchemlyb(**G_kwargs) + if estimator == 'mdpow': + G.analyze(**G_kwargs) + elif estimator == 'alchemlyb': + if G_kwargs['SI']: + logger.info("Statistical inefficiency analysis will be performed.") + else: + logger.info("Statistical inefficiency analysis won't be performed.") + G.analyze_alchemlyb(**G_kwargs) # x.Gibbs are QuantityWithError so they do error propagation transferFE = G2.results.DeltaA.Gibbs - G1.results.DeltaA.Gibbs From d1f0eeaa429a2de540b81d174c988f116bd972f9 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Wed, 28 Jul 2021 14:40:25 -0700 Subject: [PATCH 35/56] Apply suggestions from code review --- mdpow/fep.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/mdpow/fep.py b/mdpow/fep.py index 847ade3f..2eefc6f2 100644 --- a/mdpow/fep.py +++ b/mdpow/fep.py @@ -134,6 +134,7 @@ import scipy.integrate import six +from six.moves import zip from scipy import constants import numkit.integration import numkit.timeseries @@ -152,11 +153,7 @@ from numkit.observables import QuantityWithError from glob import glob -# Patch due to slightly different library names -try: - from ConfigParser import NoOptionError -except ImportError: - from configparser import NoOptionError +from six.moves.configparser import NoOptionError import logging logger = logging.getLogger('mdpow.fep') @@ -865,7 +862,6 @@ def contains_corrupted_xvgs(self): :attr:`Gsolv._corrupted` as dicts of dicts with the component as primary and the lambda as secondary key. """ - from six.moves import zip def _lencorrupted(xvg): try: return len(xvg.corrupted_lineno) From e81062f842e2a2113ec3fc30998ab1cd8dd77ec4 Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:32:39 -0700 Subject: [PATCH 36/56] Update CHANGES Co-authored-by: Oliver Beckstein --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index e6bab251..abfb9637 100644 --- a/CHANGES +++ b/CHANGES @@ -11,7 +11,7 @@ orbeckst, VOD555, ALescoulie Changes * renamed package to MDPOW -* support python 3.7 -- 3.9 +* support python 3.7 -- 3.9 (#84) * tested with GROMACS 4.6.5, 2018.6, 2020.6, 2021.1 (PR #159, #164) * removed all generated docs from package * config parser MERGES user runinput.yml with the package defaults From 7a2139d601aae31e541d97bc8a33566e126b8d70 Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:03:30 -0700 Subject: [PATCH 37/56] Remove logger patch --- mdpow/config.py | 3 --- mdpow/equil.py | 2 -- mdpow/fep.py | 2 -- mdpow/forcefields.py | 2 -- mdpow/restart.py | 2 -- mdpow/run.py | 2 -- 6 files changed, 13 deletions(-) diff --git a/mdpow/config.py b/mdpow/config.py index 48b016ec..5220d4a9 100644 --- a/mdpow/config.py +++ b/mdpow/config.py @@ -102,9 +102,6 @@ import logging logger = logging.getLogger("mdpow.config") -if not hasattr(logger, "warning"): - logger.warning = logger.warn - # Reading of configuration files # ------------------------------ diff --git a/mdpow/equil.py b/mdpow/equil.py index f6885f08..20222319 100644 --- a/mdpow/equil.py +++ b/mdpow/equil.py @@ -50,8 +50,6 @@ import logging logger = logging.getLogger('mdpow.equil') -if not hasattr(logger, "warning"): - logger.warning = logger.warn # ITP <-- forcefields.get_solvent_model(id).itp # BOX <-- forcefields.get_solvent_model(id).coordinates diff --git a/mdpow/fep.py b/mdpow/fep.py index 2eefc6f2..1b858759 100644 --- a/mdpow/fep.py +++ b/mdpow/fep.py @@ -157,8 +157,6 @@ import logging logger = logging.getLogger('mdpow.fep') -if not hasattr(logger, "warning"): - logger.warning = logger.warn from . import config from .restart import Journalled diff --git a/mdpow/forcefields.py b/mdpow/forcefields.py index 6a1bf79a..e7683e8b 100644 --- a/mdpow/forcefields.py +++ b/mdpow/forcefields.py @@ -56,8 +56,6 @@ import logging logger = logging.getLogger("mdpow.forecefields") -if not hasattr(logger, "warning"): - logger.warning = logger.warn #: Default force field. At the moment, only OPLS-AA is directly #: supported. diff --git a/mdpow/restart.py b/mdpow/restart.py index b24d10ee..ca1fa1fc 100644 --- a/mdpow/restart.py +++ b/mdpow/restart.py @@ -29,8 +29,6 @@ import logging logger = logging.getLogger('mdpow.checkpoint') -if not hasattr(logger, "warning"): - logger.warning = logger.warn def checkpoint(name, sim, filename): """Execute the :meth:`Journalled.save` method and log the event.""" diff --git a/mdpow/run.py b/mdpow/run.py index 79779574..2c3505cb 100644 --- a/mdpow/run.py +++ b/mdpow/run.py @@ -52,8 +52,6 @@ import logging logger = logging.getLogger('mdpow.run') -if not hasattr(logger, "warning"): - logger.warning = logger.warn def setupMD(S, protocol, cfg): From 5a9193250df96c5b0036b3aa50b8f7a9dceb924c Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:14:02 -0700 Subject: [PATCH 38/56] Update ci.yaml --- .github/workflows/ci.yaml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1dd9700d..4b99caaa 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,9 +24,9 @@ jobs: matrix: os: [ubuntu-latest, macOS-latest] #python-version: [2.7] - python-version: [2.7, 3.8] + #python-version: [2.7, 3.8] #python-version: [2.7, 3.6, 3.7, 3.8, 3.9] - gromacs-version: ["4.6.5", "2018.6", "2020.6", "2021.1"] + #gromacs-version: ["4.6.5", "2018.6", "2020.6", "2021.1"] # only test one GROMACS version on macOS to keep the testing # matrix manageable exclude: @@ -36,6 +36,18 @@ jobs: gromacs-version: "2018.6" - os: macOS-latest gromacs-version: "2020.6" + include: + - os: macOS-latest + gromacs-version: "2021.1" + - os: ubuntu-latest + python-version: 3.7 + gromacs-version: "2021.1" + - os: ubuntu-latest + python-version: 3.8 + gromacs-version: "2021.1" + - os: ubuntu-latest + python-version: 3.9 + gromacs-version: "2021.1" env: MPLBACKEND: agg From 6d5cdcb983c085e943d4e9158e0f503385d6ee82 Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:14:51 -0700 Subject: [PATCH 39/56] Update ci.yaml --- .github/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4b99caaa..2b6158f0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,9 +24,9 @@ jobs: matrix: os: [ubuntu-latest, macOS-latest] #python-version: [2.7] - #python-version: [2.7, 3.8] + python-version: [2.7, 3.8] #python-version: [2.7, 3.6, 3.7, 3.8, 3.9] - #gromacs-version: ["4.6.5", "2018.6", "2020.6", "2021.1"] + gromacs-version: ["4.6.5", "2018.6", "2020.6", "2021.1"] # only test one GROMACS version on macOS to keep the testing # matrix manageable exclude: From 01d3e408a19484db23952ce7e1741ef289ac7ad8 Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:16:26 -0700 Subject: [PATCH 40/56] Add files via upload --- .github/workflows/ci.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1dd9700d..2b6158f0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -36,6 +36,18 @@ jobs: gromacs-version: "2018.6" - os: macOS-latest gromacs-version: "2020.6" + include: + - os: macOS-latest + gromacs-version: "2021.1" + - os: ubuntu-latest + python-version: 3.7 + gromacs-version: "2021.1" + - os: ubuntu-latest + python-version: 3.8 + gromacs-version: "2021.1" + - os: ubuntu-latest + python-version: 3.9 + gromacs-version: "2021.1" env: MPLBACKEND: agg From ad3e5860896c74cee1d30aeafc3724f6b11b66a8 Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:19:26 -0700 Subject: [PATCH 41/56] Adding six to dependencies --- INSTALL.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/INSTALL.rst b/INSTALL.rst index 0d04a3c6..0d769817 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -33,7 +33,7 @@ To make a conda environment with the latest packages for Python 2.7 and name it *mdpow*; this installs the larger dependencies that are pre-requisites for MDPOW:: - conda create -c conda-forge -n mdpow python=2.7 numpy scipy 'matplotlib<3.3' 'mdanalysis<2' 'mdanalysistests<2' pyyaml + conda create -c conda-forge -n mdpow python=2.7 numpy scipy 'matplotlib<3.3' 'mdanalysis<2' 'mdanalysistests<2' pyyaml six conda activate mdpow pip install gromacswrapper @@ -43,7 +43,7 @@ For Python 3.7 and up. *Note:* with Pandas version 1.3 there is an error with `Alchemlyb `_ (see `issue #147 `_) which will be fixed in Alchemlyb 0.5.:: - conda create -c conda-forge -n mdpow python=3.7 numpy scipy 'matplotlib' 'mdanalysis' 'mdanalysistests' pyyaml + conda create -c conda-forge -n mdpow python=3.7 numpy scipy 'matplotlib' 'mdanalysis' 'mdanalysistests' pyyaml six conda activate mdpow pip install gromacswrapper From 34bb10b0c365eda777ec5d0f6f34c3dd32d4256f Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:28:08 -0700 Subject: [PATCH 42/56] Fixing indents fep.py --- mdpow/fep.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mdpow/fep.py b/mdpow/fep.py index 1b858759..5a4639cb 100644 --- a/mdpow/fep.py +++ b/mdpow/fep.py @@ -1389,14 +1389,14 @@ def p_transfer(G1, G2, **kwargs): logger.info("Estimator is %s.", estimator) logger.info("Free energy calculation method is %s.", G.method) - if estimator == 'mdpow': - G.analyze(**G_kwargs) - elif estimator == 'alchemlyb': - if G_kwargs['SI']: - logger.info("Statistical inefficiency analysis will be performed.") - else: - logger.info("Statistical inefficiency analysis won't be performed.") - G.analyze_alchemlyb(**G_kwargs) + if estimator == 'mdpow': + G.analyze(**G_kwargs) + elif estimator == 'alchemlyb': + if G_kwargs['SI']: + logger.info("Statistical inefficiency analysis will be performed.") + else: + logger.info("Statistical inefficiency analysis won't be performed.") + G.analyze_alchemlyb(**G_kwargs) # x.Gibbs are QuantityWithError so they do error propagation transferFE = G2.results.DeltaA.Gibbs - G1.results.DeltaA.Gibbs From c0d5da188d35ba6b97f3fe2ee41eaa9024303ea6 Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:34:07 -0700 Subject: [PATCH 43/56] Removing tempdir imports --- mdpow/tests/test_analysis.py | 2 -- mdpow/tests/test_analysis_alchemlyb.py | 2 -- mdpow/tests/test_fep.py | 2 -- mdpow/tests/test_filelock.py | 2 -- mdpow/tests/test_forcefields.py | 2 -- mdpow/tests/test_runinput.py | 2 -- mdpow/tests/test_solvation.py | 2 -- 7 files changed, 14 deletions(-) diff --git a/mdpow/tests/test_analysis.py b/mdpow/tests/test_analysis.py index 2854a41d..8b4179a7 100644 --- a/mdpow/tests/test_analysis.py +++ b/mdpow/tests/test_analysis.py @@ -1,7 +1,5 @@ from __future__ import absolute_import -from . import tempdir - import os.path import sys diff --git a/mdpow/tests/test_analysis_alchemlyb.py b/mdpow/tests/test_analysis_alchemlyb.py index 2d8378ad..fecd6738 100644 --- a/mdpow/tests/test_analysis_alchemlyb.py +++ b/mdpow/tests/test_analysis_alchemlyb.py @@ -1,7 +1,5 @@ from __future__ import absolute_import -from . import tempdir - import os.path import sys diff --git a/mdpow/tests/test_fep.py b/mdpow/tests/test_fep.py index 78acd1fd..841fd65f 100644 --- a/mdpow/tests/test_fep.py +++ b/mdpow/tests/test_fep.py @@ -5,8 +5,6 @@ import pytest -from . import tempdir - import numpy as np from numpy.testing import assert_array_almost_equal, assert_almost_equal from scipy import constants diff --git a/mdpow/tests/test_filelock.py b/mdpow/tests/test_filelock.py index d5f37917..dec0b1cb 100644 --- a/mdpow/tests/test_filelock.py +++ b/mdpow/tests/test_filelock.py @@ -1,7 +1,5 @@ from __future__ import absolute_import -from . import tempdir - import os.path import pytest diff --git a/mdpow/tests/test_forcefields.py b/mdpow/tests/test_forcefields.py index 96f508ef..ad7d26f3 100644 --- a/mdpow/tests/test_forcefields.py +++ b/mdpow/tests/test_forcefields.py @@ -1,7 +1,5 @@ from __future__ import absolute_import -from . import tempdir - import os.path import pytest diff --git a/mdpow/tests/test_runinput.py b/mdpow/tests/test_runinput.py index 7a8f0682..07662175 100644 --- a/mdpow/tests/test_runinput.py +++ b/mdpow/tests/test_runinput.py @@ -1,7 +1,5 @@ from __future__ import absolute_import -from . import tempdir - import os.path import numpy as np diff --git a/mdpow/tests/test_solvation.py b/mdpow/tests/test_solvation.py index b2a3b6da..5f3a7ec9 100644 --- a/mdpow/tests/test_solvation.py +++ b/mdpow/tests/test_solvation.py @@ -1,7 +1,5 @@ from __future__ import absolute_import -from . import tempdir - import os import shutil From 8d25e943aceaff5d974d5a00e83da425e6dfa286 Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:46:13 -0700 Subject: [PATCH 44/56] Merge branch 'Becksteinlab:develop' into python_3 --- .github/workflows/ci.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2b6158f0..bcae8e44 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -26,7 +26,8 @@ jobs: #python-version: [2.7] python-version: [2.7, 3.8] #python-version: [2.7, 3.6, 3.7, 3.8, 3.9] - gromacs-version: ["4.6.5", "2018.6", "2020.6", "2021.1"] + #gromacs-version: ["4.6.5", "2018.6", "2020.6", "2021.1"] + gromacs-version: ["2021.1"] # only test one GROMACS version on macOS to keep the testing # matrix manageable exclude: @@ -38,6 +39,7 @@ jobs: gromacs-version: "2020.6" include: - os: macOS-latest + python-version 3.9 gromacs-version: "2021.1" - os: ubuntu-latest python-version: 3.7 From f02dd653a74fada4bbd99e559eb17125e8fbdd9f Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:46:38 -0700 Subject: [PATCH 45/56] Add files via upload --- .github/workflows/ci.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2b6158f0..bcae8e44 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -26,7 +26,8 @@ jobs: #python-version: [2.7] python-version: [2.7, 3.8] #python-version: [2.7, 3.6, 3.7, 3.8, 3.9] - gromacs-version: ["4.6.5", "2018.6", "2020.6", "2021.1"] + #gromacs-version: ["4.6.5", "2018.6", "2020.6", "2021.1"] + gromacs-version: ["2021.1"] # only test one GROMACS version on macOS to keep the testing # matrix manageable exclude: @@ -38,6 +39,7 @@ jobs: gromacs-version: "2020.6" include: - os: macOS-latest + python-version 3.9 gromacs-version: "2021.1" - os: ubuntu-latest python-version: 3.7 From 2154e686ba8714c0763b129643de5d29f7374463 Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:52:48 -0700 Subject: [PATCH 46/56] Add files via upload --- .github/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bcae8e44..99192066 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,8 +24,8 @@ jobs: matrix: os: [ubuntu-latest, macOS-latest] #python-version: [2.7] - python-version: [2.7, 3.8] - #python-version: [2.7, 3.6, 3.7, 3.8, 3.9] + #python-version: [2.7, 3.8] + python-version: [2.7, 3.6, 3.7, 3.8, 3.9] #gromacs-version: ["4.6.5", "2018.6", "2020.6", "2021.1"] gromacs-version: ["2021.1"] # only test one GROMACS version on macOS to keep the testing From 2573d5a1db5c89184bf64d9597775a7f21e6d5a1 Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:58:50 -0700 Subject: [PATCH 47/56] Merge branch 'Becksteinlab:develop' into python_3 --- .github/workflows/ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bcae8e44..99192066 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,8 +24,8 @@ jobs: matrix: os: [ubuntu-latest, macOS-latest] #python-version: [2.7] - python-version: [2.7, 3.8] - #python-version: [2.7, 3.6, 3.7, 3.8, 3.9] + #python-version: [2.7, 3.8] + python-version: [2.7, 3.6, 3.7, 3.8, 3.9] #gromacs-version: ["4.6.5", "2018.6", "2020.6", "2021.1"] gromacs-version: ["2021.1"] # only test one GROMACS version on macOS to keep the testing From 070f37b5a058c4579d7bb5f1386a0b6bc2f38e56 Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 15:04:40 -0700 Subject: [PATCH 48/56] add deepcopy test in test_fep.py --- mdpow/tests/test_fep.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/mdpow/tests/test_fep.py b/mdpow/tests/test_fep.py index 841fd65f..971b1fbe 100644 --- a/mdpow/tests/test_fep.py +++ b/mdpow/tests/test_fep.py @@ -8,6 +8,7 @@ import numpy as np from numpy.testing import assert_array_almost_equal, assert_almost_equal from scipy import constants +from copy import deepcopy import mdpow import mdpow.fep @@ -65,6 +66,26 @@ def test_VDW(self): def test_Coulomb(self): return self._test_schedule('Coulomb') + @pytest.mark.parametrize('component', ['VDW', 'Coulomb']) + def test_copy(self, component): + section = 'FEP_schedule_{0}'.format(component) + schedule = deepcopy(mdpow.fep.FEPschedule.load(self.cfg, section)) + reference = self.reference[component] + + for k in schedule: + assert k in reference, "additional entry {0} in runinput.yml".format(k) + + for k in reference: + assert k in schedule, "missing entry {0} in runinput.yml".format(k) + + for k in schedule.keys(): + if k == "lambdas": + assert_array_almost_equal(schedule[k], reference[k], + err_msg="FEP schedule {0} mismatch".format(k)) + else: + assert schedule[k] == reference[k], \ + "mismatch between loaded FEP schedule entry {0} and reference".format(k) + def _test_schedule(self, component): section = 'FEP_schedule_{0}'.format(component) schedule = mdpow.fep.FEPschedule.load(self.cfg, section) From ac59bc5edefd8e9ec3f3ead9a2c27eafdfe55d6b Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 15:36:48 -0700 Subject: [PATCH 49/56] Update ci.yaml --- .github/workflows/ci.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 99192066..1c7be722 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -39,17 +39,17 @@ jobs: gromacs-version: "2020.6" include: - os: macOS-latest - python-version 3.9 - gromacs-version: "2021.1" + python-version: 3.9 + gromacs-version: "2021.1" - os: ubuntu-latest - python-version: 3.7 - gromacs-version: "2021.1" + python-version: 3.7 + gromacs-version: "2021.1" - os: ubuntu-latest - python-version: 3.8 - gromacs-version: "2021.1" + python-version: 3.8 + gromacs-version: "2021.1" - os: ubuntu-latest - python-version: 3.9 - gromacs-version: "2021.1" + python-version: 3.9 + gromacs-version: "2021.1" env: MPLBACKEND: agg From c6f367df0eab2d6658ed9e8596fc23b24c08148b Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 15:40:50 -0700 Subject: [PATCH 50/56] Update ci.yaml --- .github/workflows/ci.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1c7be722..30a51df5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -37,6 +37,12 @@ jobs: gromacs-version: "2018.6" - os: macOS-latest gromacs-version: "2020.6" + - os: macOS-latest + python-version: 3.8 + - os: macOS-latest + python-version: 3.7 + - os: macOS-latest + python-version: 3.6 include: - os: macOS-latest python-version: 3.9 From 573cca8d1ecb4eb50f3a9ccc2ced606e49e03022 Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 16:15:21 -0700 Subject: [PATCH 51/56] add config.py coverage --- mdpow/tests/test_fep.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mdpow/tests/test_fep.py b/mdpow/tests/test_fep.py index 971b1fbe..32cbc8f7 100644 --- a/mdpow/tests/test_fep.py +++ b/mdpow/tests/test_fep.py @@ -10,7 +10,7 @@ from scipy import constants from copy import deepcopy -import mdpow +import mdpow.config import mdpow.fep import gromacs @@ -86,6 +86,12 @@ def test_copy(self, component): assert schedule[k] == reference[k], \ "mismatch between loaded FEP schedule entry {0} and reference".format(k) + @pytest.mark.parametrize('component', ['VDW', 'Coulomb']) + def test_write(self, component): + self.cfg.write('cfg.yaml') + new_cfg = mdpow.config.get_configuration('cfg.yaml') + assert new_cfg.conf == self.cfg.conf + def _test_schedule(self, component): section = 'FEP_schedule_{0}'.format(component) schedule = mdpow.fep.FEPschedule.load(self.cfg, section) From 64529dfa06f1186d4bdcab5b963a8606b9f165d1 Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 16:28:10 -0700 Subject: [PATCH 52/56] add config.py coverage --- mdpow/tests/test_fep.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mdpow/tests/test_fep.py b/mdpow/tests/test_fep.py index 32cbc8f7..54eeba97 100644 --- a/mdpow/tests/test_fep.py +++ b/mdpow/tests/test_fep.py @@ -92,6 +92,11 @@ def test_write(self, component): new_cfg = mdpow.config.get_configuration('cfg.yaml') assert new_cfg.conf == self.cfg.conf + def test_get(self): + tmp_cfg = mdpow.config.POWConfigParser() + item = tmp_cfg.get('VDW', 'label') + assert item is None + def _test_schedule(self, component): section = 'FEP_schedule_{0}'.format(component) schedule = mdpow.fep.FEPschedule.load(self.cfg, section) From 671abedc1097ce06a3e8516bf42d482258d4cf3c Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Thu, 29 Jul 2021 17:13:32 -0700 Subject: [PATCH 53/56] adjusted testing matrix --- .github/workflows/ci.yaml | 45 +++++++++++++++------------------------ CHANGES | 2 +- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 30a51df5..7fbc8547 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,40 +22,29 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macOS-latest] - #python-version: [2.7] - #python-version: [2.7, 3.8] - python-version: [2.7, 3.6, 3.7, 3.8, 3.9] - #gromacs-version: ["4.6.5", "2018.6", "2020.6", "2021.1"] - gromacs-version: ["2021.1"] - # only test one GROMACS version on macOS to keep the testing - # matrix manageable - exclude: - - os: macOS-latest - gromacs-version: "4.6.5" - - os: macOS-latest - gromacs-version: "2018.6" - - os: macOS-latest - gromacs-version: "2020.6" - - os: macOS-latest - python-version: 3.8 - - os: macOS-latest - python-version: 3.7 - - os: macOS-latest - python-version: 3.6 + # only test all GROMACS version on the oldest and latest + # Python to keep the testing matrix manageable and only use 2 + # macos runners (latest GROMACS, oldes and latest Python) + + os: [ubuntu-latest] + python-version: [2.7, 3.9] + gromacs-version: ["4.6.5", "2018.6", "2020.6", "2021.1"] include: - - os: macOS-latest - python-version: 3.9 - gromacs-version: "2021.1" + - os: ubuntu-latest + python-version: 3.6 + gromacs-version: 2021.1 - os: ubuntu-latest python-version: 3.7 - gromacs-version: "2021.1" + gromacs-version: 2021.1 - os: ubuntu-latest python-version: 3.8 - gromacs-version: "2021.1" - - os: ubuntu-latest + gromacs-version: 2021.1 + - os: macos-latest + python-version: 2.7 + gromacs-version: 2021.1 + - os: macos-latest python-version: 3.9 - gromacs-version: "2021.1" + gromacs-version: 2021.1 env: MPLBACKEND: agg diff --git a/CHANGES b/CHANGES index abfb9637..0684102d 100644 --- a/CHANGES +++ b/CHANGES @@ -11,7 +11,7 @@ orbeckst, VOD555, ALescoulie Changes * renamed package to MDPOW -* support python 3.7 -- 3.9 (#84) +* support Python 3.7 -- 3.9 on Linux and macOS (#84) * tested with GROMACS 4.6.5, 2018.6, 2020.6, 2021.1 (PR #159, #164) * removed all generated docs from package * config parser MERGES user runinput.yml with the package defaults From 1fa47036135a630db1f7623f18a615a56bae0118 Mon Sep 17 00:00:00 2001 From: Alia Lescoulie <72271618+ALescoulie@users.noreply.github.com> Date: Thu, 29 Jul 2021 17:55:49 -0700 Subject: [PATCH 54/56] add config.py coverage --- mdpow/tests/test_fep.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mdpow/tests/test_fep.py b/mdpow/tests/test_fep.py index 54eeba97..d63282c9 100644 --- a/mdpow/tests/test_fep.py +++ b/mdpow/tests/test_fep.py @@ -97,6 +97,9 @@ def test_get(self): item = tmp_cfg.get('VDW', 'label') assert item is None + def test_iterable(self): + assert not mdpow.config.iterable('test') + def _test_schedule(self, component): section = 'FEP_schedule_{0}'.format(component) schedule = mdpow.fep.FEPschedule.load(self.cfg, section) From 808f1e72b7f0fabc92ebc517a5f92c6e9def2e59 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Thu, 29 Jul 2021 18:11:30 -0700 Subject: [PATCH 55/56] updated import statements in mdpow modules --- mdpow/config.py | 5 +++-- mdpow/equil.py | 5 +++-- mdpow/fep.py | 19 ++++++++++--------- mdpow/restart.py | 3 ++- mdpow/run.py | 6 +++--- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/mdpow/config.py b/mdpow/config.py index 5220d4a9..382c42eb 100644 --- a/mdpow/config.py +++ b/mdpow/config.py @@ -90,12 +90,13 @@ """ -from __future__ import absolute_import +from __future__ import absolute_import, division + +import six import os, errno from pkg_resources import resource_filename, resource_listdir import yaml -import six import numpy as np import gromacs.utilities diff --git a/mdpow/equil.py b/mdpow/equil.py index 20222319..259aa357 100644 --- a/mdpow/equil.py +++ b/mdpow/equil.py @@ -29,11 +29,12 @@ .. autodata:: DIST """ -from __future__ import absolute_import, with_statement +from __future__ import absolute_import, division + +from six.moves import cPickle as pickle import os, errno import shutil -from six.moves import cPickle as pickle import MDAnalysis as mda diff --git a/mdpow/fep.py b/mdpow/fep.py index 5a4639cb..efd149c3 100644 --- a/mdpow/fep.py +++ b/mdpow/fep.py @@ -120,40 +120,41 @@ See `Free Energy Tutorial`_. """ -from __future__ import absolute_import, with_statement +from __future__ import absolute_import, division + +import six +from six.moves import zip +from six.moves.configparser import NoOptionError import os import errno import copy from subprocess import call import warnings -import sys +from glob import glob import numpy import pandas as pd import scipy.integrate -import six -from six.moves import zip from scipy import constants + import numkit.integration import numkit.timeseries +from numkit.observables import QuantityWithError from alchemlyb.parsing.gmx import extract_dHdl, extract_u_nk from alchemlyb.estimators import TI, BAR, MBAR from alchemlyb.parsing.gmx import _extract_dataframe from pymbar.timeseries import (statisticalInefficiency, subsampleCorrelatedData, ) -import gromacs, gromacs.utilities +import gromacs +import gromacs.utilities try: import gromacs.setup except (ImportError, OSError): raise ImportError("Gromacs installation not found, source GMXRC?") from gromacs.utilities import asiterable, AttributeDict, in_dir, openany -from numkit.observables import QuantityWithError -from glob import glob - -from six.moves.configparser import NoOptionError import logging logger = logging.getLogger('mdpow.fep') diff --git a/mdpow/restart.py b/mdpow/restart.py index ca1fa1fc..1fb77c71 100644 --- a/mdpow/restart.py +++ b/mdpow/restart.py @@ -23,9 +23,10 @@ """ from __future__ import absolute_import +from six.moves import cPickle as pickle + import os import errno -from six.moves import cPickle as pickle import logging logger = logging.getLogger('mdpow.checkpoint') diff --git a/mdpow/run.py b/mdpow/run.py index 2c3505cb..7b2282b5 100644 --- a/mdpow/run.py +++ b/mdpow/run.py @@ -39,6 +39,8 @@ from __future__ import absolute_import +from six.moves import configparser + import sys import os import errno @@ -71,8 +73,6 @@ def setupMD(S, protocol, cfg): def get_mdp_files(cfg, protocols): """Get file names of MDP files from *cfg* for all *protocols*""" - import configparser - mdpfiles = {} for protocol in protocols: try: @@ -320,7 +320,7 @@ def fep_simulation(cfg, solvent, **kwargs): logger.info("Running FEP with saved settings from %r", savefilename) logger.info("Note: all configuration file options are ignored!") else: - # method to be used "TI"/"BAR" + # method to be used "TI"/"BAR" method = cfg.get("FEP", "method") logger.info("Running FEP with the %r implementation in Gromacs", method) # custom mdp file From de437f2afe87c9141eedbbd3631c43bf37adec38 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Thu, 29 Jul 2021 18:12:07 -0700 Subject: [PATCH 56/56] mdpow scripts Python3 compatibility --- scripts/mdpow-cfg2yaml.py | 1 + scripts/mdpow-check | 11 ++++++----- scripts/mdpow-equilibrium | 1 + scripts/mdpow-fep | 1 + scripts/mdpow-get-runinput | 1 + scripts/mdpow-ghyd | 5 ++--- scripts/mdpow-pcw | 5 ++--- scripts/mdpow-pow | 5 ++--- scripts/mdpow-rebuild-fep | 7 ++++--- scripts/mdpow-rebuild-simulation | 3 ++- scripts/mdpow-solvationenergy | 5 ++--- 11 files changed, 24 insertions(+), 21 deletions(-) diff --git a/scripts/mdpow-cfg2yaml.py b/scripts/mdpow-cfg2yaml.py index a46f7c4c..ef5efac0 100755 --- a/scripts/mdpow-cfg2yaml.py +++ b/scripts/mdpow-cfg2yaml.py @@ -1,4 +1,5 @@ #! /bin/python +from __future__ import absolute_import, print_statement, division import sys import yaml diff --git a/scripts/mdpow-check b/scripts/mdpow-check index 25f609f1..ccd50eab 100755 --- a/scripts/mdpow-check +++ b/scripts/mdpow-check @@ -9,6 +9,7 @@ error. A quick way to find problems is to do a :: cat status.txt | gawk -F '|' '$2 !~ /OK/ {print $0}' """ +from __future__ import absolute_import, print_statement, division import logging logger = logging.getLogger('mdpow') @@ -28,7 +29,7 @@ if __name__ == "__main__": metavar="FILE") parser.set_defaults(outfile="status.txt") opts,args = parser.parse_args() - + logger.info("Writing status to %r", opts.outfile) # dir status type comment @@ -36,7 +37,7 @@ if __name__ == "__main__": with open(opts.outfile, 'w') as status: def swrite(*args): all_args = tuple([directory] + list(args)) - status.write(fmt % all_args) + status.write(fmt % all_args) sys.stderr.write(fmt % all_args) for directory in args: score = 0 @@ -47,13 +48,13 @@ if __name__ == "__main__": # directory itself if not os.path.exists(directory): swrite("NOTFOUND", "directory", "not found") - logger.warn("Directory %r not found, skipping...", directory) + logger.warning("Directory %r not found, skipping...", directory) continue # output from simulations solvents = ('water', 'octanol') nmin_fepsims = {'Coulomb':5, 'VDW':16} - for solvent in solvents: + for solvent in solvents: for feptype,nmin in nmin_fepsims.items(): xvg = os.path.join( directory, 'FEP', solvent, @@ -76,7 +77,7 @@ if __name__ == "__main__": swrite("OK", "pickle", fn) else: swrite("NOTFOUND", "pickle", picklefile) - + # combined graph from mdpow-pow graphs = glob(os.path.join(directory, 'dVdl_*.*')) if len(graphs) > 0: diff --git a/scripts/mdpow-equilibrium b/scripts/mdpow-equilibrium index f8523e97..1d93b4cf 100755 --- a/scripts/mdpow-equilibrium +++ b/scripts/mdpow-equilibrium @@ -21,6 +21,7 @@ You will require: # TODO: # # 2) Default config file in user's home dir. +from __future__ import absolute_import, print_statement, division from mdpow.run import equilibrium_simulation from mdpow.config import get_configuration diff --git a/scripts/mdpow-fep b/scripts/mdpow-fep index 3cc37a40..dc9bc9b4 100755 --- a/scripts/mdpow-fep +++ b/scripts/mdpow-fep @@ -20,6 +20,7 @@ You will require: # TODO: # # 2) Default config file in user's home dir. +from __future__ import absolute_import, print_statement, division from mdpow.run import fep_simulation from mdpow.config import get_configuration diff --git a/scripts/mdpow-get-runinput b/scripts/mdpow-get-runinput index 91edf186..eba22629 100755 --- a/scripts/mdpow-get-runinput +++ b/scripts/mdpow-get-runinput @@ -3,6 +3,7 @@ Generate a template RUNINPUTFILE. """ +from __future__ import absolute_import, print_statement, division from mdpow.config import get_configuration diff --git a/scripts/mdpow-ghyd b/scripts/mdpow-ghyd index 24d6faf2..bf2cbe3f 100755 --- a/scripts/mdpow-ghyd +++ b/scripts/mdpow-ghyd @@ -49,8 +49,7 @@ directory folder in which the simulations were stored """ - -from __future__ import with_statement +from __future__ import absolute_import, print_statement, division import os import warnings @@ -221,7 +220,7 @@ if __name__ == "__main__": for directory in args: if not os.path.exists(directory): - logger.warn("Directory %r not found, skipping...", directory) + logger.warning("Directory %r not found, skipping...", directory) continue logger.info("Analyzing directory %r... (can take a while)", directory) diff --git a/scripts/mdpow-pcw b/scripts/mdpow-pcw index c0af1f41..48d03755 100755 --- a/scripts/mdpow-pcw +++ b/scripts/mdpow-pcw @@ -32,8 +32,7 @@ wat_ok, cyc_ok directory folder in which the simulations were stored """ - -from __future__ import with_statement +from __future__ import absolute_import, print_statement, division import os import mdpow.fep @@ -249,7 +248,7 @@ if __name__ == "__main__": for directory in opts.directory: if not os.path.exists(directory): - logger.warn("Directory %r not found, skipping...", directory) + logger.warning("Directory %r not found, skipping...", directory) continue logger.info("Analyzing directory %r... (can take a while)", directory) diff --git a/scripts/mdpow-pow b/scripts/mdpow-pow index 1bd95223..8edb8d17 100755 --- a/scripts/mdpow-pow +++ b/scripts/mdpow-pow @@ -32,8 +32,7 @@ wat_ok, octa_ok directory folder in which the simulations were stored """ - -from __future__ import with_statement +from __future__ import absolute_import, print_statement, division import os import mdpow.fep @@ -249,7 +248,7 @@ if __name__ == "__main__": for directory in opts.directory: if not os.path.exists(directory): - logger.warn("Directory %r not found, skipping...", directory) + logger.warning("Directory %r not found, skipping...", directory) continue logger.info("Analyzing directory %r... (can take a while)", directory) diff --git a/scripts/mdpow-rebuild-fep b/scripts/mdpow-rebuild-fep index 8bf2e8a3..615d3dab 100755 --- a/scripts/mdpow-rebuild-fep +++ b/scripts/mdpow-rebuild-fep @@ -7,6 +7,7 @@ equilibrium simulation file under /FEP. (This should only be necessary when the fep file was destroyed due to a software error.) """ +from __future__ import absolute_import, print_statement, division import os import mdpow.equil @@ -30,8 +31,8 @@ def fixFEP(solvent, fepfile, simfile, basedir): G = Gsolv[solvent](simulation=S, basedir=basedir, permissive=True) except Exception as err: logger.error(str(err)) - logger.warn("Could not get the simulation pickle file %(simfile)r", vars()) - logger.warn("The FEP pickle file will be fixed as it is (no path adjustments)") + logger.warning("Could not get the simulation pickle file %(simfile)r", vars()) + logger.warning("The FEP pickle file will be fixed as it is (no path adjustments)") G = Gsolv[solvent](filename=fepfile) # remove pre-0.2 standard-state correction (wrong!) and pdV (wrong) @@ -96,7 +97,7 @@ if __name__ == "__main__": logger.debug("%r is not a directory, skipping!", directory) continue if not os.path.exists(directory): - logger.warn("Directory %r not found, skipping!", directory) + logger.warning("Directory %r not found, skipping!", directory) continue logger.info("Rebuilding under directory %r... (can take a while)", directory) with in_dir(directory, create=False): diff --git a/scripts/mdpow-rebuild-simulation b/scripts/mdpow-rebuild-simulation index 980b82b2..3d4f8e28 100755 --- a/scripts/mdpow-rebuild-simulation +++ b/scripts/mdpow-rebuild-simulation @@ -7,6 +7,7 @@ adjusted paths. (This should only be necessary when the directories are moved or copied to a different machine.) """ +from __future__ import absolute_import, print_statement, division import os import mdpow.equil @@ -58,7 +59,7 @@ if __name__ == "__main__": logger.debug("%r is not a directory, skipping!", directory) continue if not os.path.exists(directory): - logger.warn("Directory %r not found, skipping!", directory) + logger.warning("Directory %r not found, skipping!", directory) continue logger.info("Rebuilding under directory %r... (can take a while)", directory) with in_dir(directory, create=False): diff --git a/scripts/mdpow-solvationenergy b/scripts/mdpow-solvationenergy index e93dac19..846b9e23 100755 --- a/scripts/mdpow-solvationenergy +++ b/scripts/mdpow-solvationenergy @@ -45,8 +45,7 @@ directory folder in which the simulations were stored """ - -from __future__ import with_statement +from __future__ import absolute_import, print_statement, division import os import mdpow.fep @@ -282,7 +281,7 @@ if __name__ == "__main__": for directory in opts.directory: if not os.path.exists(directory): - logger.warn("Directory %r not found, skipping...", directory) + logger.warning("Directory %r not found, skipping...", directory) continue logger.info("Analyzing directory %r... (can take a while)", directory)