From 8d1108fd0286b231d190baee4022d34d6149f4b3 Mon Sep 17 00:00:00 2001 From: "Jessica A. Nash" Date: Wed, 5 Sep 2018 15:02:14 -0400 Subject: [PATCH 1/8] FL: change from pandas to_hdf to append for appending to already existing hdf files --- eex/filelayer.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/eex/filelayer.py b/eex/filelayer.py index 184ff10..02f5bf2 100644 --- a/eex/filelayer.py +++ b/eex/filelayer.py @@ -75,7 +75,11 @@ def add_table(self, key, data): do_append = False self.created_tables.append(key) - data.to_hdf(self.store, key, format="t", append=do_append) + if do_append: + self.store.append(key, data) + else: + data.to_hdf(self.store, key, format="t", append=do_append) + return True def read_table(self, key, rows=None, where=None, chunksize=None): @@ -128,6 +132,11 @@ def remove_table(self, key, index=None): for i in ret: self.store.remove(key, start=i[0], stop=i[-1]) + # If everything is removed by index, we should drop this key from the list of created tables + if self.read_table(key).empty: + self.created_tables.remove(key) + + def close(self): """ Closes the FL file. From ca86253698e84e5ad4299141aa72ca310bce93c4 Mon Sep 17 00:00:00 2001 From: "Jessica A. Nash" Date: Wed, 5 Sep 2018 15:02:49 -0400 Subject: [PATCH 2/8] tests: add test for removing and re-adding terms to DL --- eex/tests/test_datalayer.py | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/eex/tests/test_datalayer.py b/eex/tests/test_datalayer.py index 7c32e91..8600e9d 100644 --- a/eex/tests/test_datalayer.py +++ b/eex/tests/test_datalayer.py @@ -978,3 +978,43 @@ def test_remove_terms_by_index_nonconsecutive_propogate(butane_dl): assert(dl.get_term_count(4)['total'] == 0) return True + +def test_remove_and_readd_terms(butane_dl): + dl = butane_dl() + + bonds = dl.get_terms(2) + + assert(not bonds.empty) + + dl.remove_terms(2) + + bonds_new = dl.get_terms(2) + + assert(bonds_new.empty) + assert(dl.get_term_count(2)['total'] == 0) + + dl.add_bonds(bonds) + + added_bonds = dl.get_terms(2) + + assert (not added_bonds.empty) + +def test_remove_and_readd_terms_index(butane_dl): + dl = butane_dl() + + bonds = dl.get_terms(2) + + assert(not bonds.empty) + + dl.remove_terms(2, index=[0]) + + assert(dl.get_term_count(2)['total'] == 2) + + dl.add_bonds(bonds.iloc[[0]]) + + added_bonds = dl.get_terms(2) + + assert (not added_bonds.empty) + + for col in added_bonds.columns: + assert(set(added_bonds[col].values) == set(bonds[col].values)) \ No newline at end of file From 20ec2b656bd9932c7bc45ec389fb25e6e69ad8ad Mon Sep 17 00:00:00 2001 From: "Jessica A. Nash" Date: Wed, 5 Sep 2018 16:48:07 -0400 Subject: [PATCH 3/8] test: rename test --- eex/tests/test_datalayer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/eex/tests/test_datalayer.py b/eex/tests/test_datalayer.py index 8600e9d..183f147 100644 --- a/eex/tests/test_datalayer.py +++ b/eex/tests/test_datalayer.py @@ -979,7 +979,7 @@ def test_remove_terms_by_index_nonconsecutive_propogate(butane_dl): return True -def test_remove_and_readd_terms(butane_dl): +def test_remove_and_add_terms(butane_dl): dl = butane_dl() bonds = dl.get_terms(2) @@ -999,7 +999,7 @@ def test_remove_and_readd_terms(butane_dl): assert (not added_bonds.empty) -def test_remove_and_readd_terms_index(butane_dl): +def test_remove_and_add_terms_index(butane_dl): dl = butane_dl() bonds = dl.get_terms(2) @@ -1017,4 +1017,4 @@ def test_remove_and_readd_terms_index(butane_dl): assert (not added_bonds.empty) for col in added_bonds.columns: - assert(set(added_bonds[col].values) == set(bonds[col].values)) \ No newline at end of file + assert(set(added_bonds[col].values) == set(bonds[col].values)) From fa2e0a006e3aa160357d8954bbea78462c19951a Mon Sep 17 00:00:00 2001 From: "Jessica A. Nash" Date: Wed, 5 Sep 2018 17:00:44 -0400 Subject: [PATCH 4/8] fix spelling mistake --- eex/datalayer.py | 6 +++--- eex/tests/test_datalayer.py | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/eex/datalayer.py b/eex/datalayer.py index 6034033..481e60e 100644 --- a/eex/datalayer.py +++ b/eex/datalayer.py @@ -1162,7 +1162,7 @@ def add_terms(self, order, df): # Finally store the dataframe return self.store.add_table("term" + str(order), df) - def remove_terms(self, order, index=None, propogate=False): + def remove_terms(self, order, index=None, propagate=False): """ Removes terms using a index notation. @@ -1172,7 +1172,7 @@ def remove_terms(self, order, index=None, propogate=False): The order (number of atoms) involved in the expression i.e. 2, "two" index: list The indices of the terms to be removed. If index is None, all terms of that order will be removed. - propogate: bool + propagate: bool This flag indicates if higher order terms should be removed with the removed term. Returns @@ -1186,7 +1186,7 @@ def remove_terms(self, order, index=None, propogate=False): order_list = [order] # If this action should be propagated, orders will be added to order list. - if propogate == True: + if propagate == True: order_list.extend([x for x in [3,4] if x > order]) # Figure out atom numbers for this removal. diff --git a/eex/tests/test_datalayer.py b/eex/tests/test_datalayer.py index 183f147..02a545d 100644 --- a/eex/tests/test_datalayer.py +++ b/eex/tests/test_datalayer.py @@ -877,7 +877,7 @@ def test_remove_terms_by_index(butane_dl): # Check that bonds are what we expect assert(sorted(bonds1.loc[2].values) == sorted(bonds2.values[0])) - # Here, since propogate was set to false. Angles and dihedrals remain unchanged. + # Here, since propagate was set to false. Angles and dihedrals remain unchanged. assert(dl.get_term_count(3)['total'] == 2) assert (dl.get_term_count(4)['total'] == 1) @@ -902,7 +902,7 @@ def test_remove_terms_by_index_nonconsecutive(butane_dl): # Check that bonds are what we expect assert(sorted(bonds1.loc[1].values) == sorted(bonds2.values[0])) - # Here, since propogate was set to false. Angles and dihedrals remain unchanged. + # Here, since propagate was set to false. Angles and dihedrals remain unchanged. assert(dl.get_term_count(3)['total'] == 2) assert (dl.get_term_count(4)['total'] == 1) @@ -921,7 +921,7 @@ def test_remove_terms_propagate(butane_dl): assert(dl.get_term_count(3)['total'] == 2) assert (dl.get_term_count(4)['total'] == 1) - dl.remove_terms(2, propogate=True) + dl.remove_terms(2, propagate=True) # Assert all have been removed. bonds = dl.get_terms(2) @@ -933,7 +933,7 @@ def test_remove_terms_propagate(butane_dl): return True -def test_remove_terms_by_index_propogate(butane_dl): +def test_remove_terms_by_index_propagate(butane_dl): dl = butane_dl() @@ -947,8 +947,8 @@ def test_remove_terms_by_index_propogate(butane_dl): assert(not bonds.empty) - # Remove one bond - choose to propogate this so dihedral and angle should also be removed. - dl.remove_terms(2, index=[0], propogate=True) + # Remove one bond - choose to propagate this so dihedral and angle should also be removed. + dl.remove_terms(2, index=[0], propagate=True) assert(dl.get_term_count(2)['total'] == 2) assert (dl.get_term_count(3)['total'] == 1) @@ -956,7 +956,7 @@ def test_remove_terms_by_index_propogate(butane_dl): return True -def test_remove_terms_by_index_nonconsecutive_propogate(butane_dl): +def test_remove_terms_by_index_nonconsecutive_propagate(butane_dl): dl = butane_dl() bonds = dl.get_terms(2) @@ -970,8 +970,8 @@ def test_remove_terms_by_index_nonconsecutive_propogate(butane_dl): assert (dl.get_term_count(3)['total'] == 2) assert (dl.get_term_count(4)['total'] == 1) - # Remove two bonds - choose to propogate this so dihedral and both angles should also be removed. - dl.remove_terms(2, index=[0, 2], propogate=True) + # Remove two bonds - choose to propagate this so dihedral and both angles should also be removed. + dl.remove_terms(2, index=[0, 2], propagate=True) assert(dl.get_term_count(2)['total'] == 1) assert (dl.get_term_count(3)['total'] == 0) From 9a5313bed3d81374e92e454f11ad37d449b56bab Mon Sep 17 00:00:00 2001 From: "Jessica A. Nash" Date: Wed, 5 Sep 2018 17:28:50 -0400 Subject: [PATCH 5/8] DL: rename variable --- eex/datalayer.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/eex/datalayer.py b/eex/datalayer.py index 481e60e..9e28902 100644 --- a/eex/datalayer.py +++ b/eex/datalayer.py @@ -1203,14 +1203,14 @@ def remove_terms(self, order, index=None, propagate=False): atoms = atom_df[cols] # Loop through order to be removed - for ord in order_list: + for current_order in order_list: # Get column names for order ord - cols = metadata.get_term_metadata(ord, "index_columns") + cols = metadata.get_term_metadata(current_order, "index_columns") cols = [x for x in cols if 'atom' in x] # Get terms for order ord - terms = self.get_terms(ord) + terms = self.get_terms(current_order) # If atoms list is empty, remove_index = None (ie, all removed). Otherwise, only remove interactions # for specified atoms. @@ -1227,23 +1227,23 @@ def remove_terms(self, order, index=None, propagate=False): remove_index.extend(matching_ind) # Use FL remove function. - self.store.remove_table("term" + str(ord), remove_index) + self.store.remove_table("term" + str(current_order), remove_index) - df = self.get_terms(ord) + df = self.get_terms(current_order) # Redo term count - self._term_count[ord] = {} - self._term_count[ord]["total"] = 0 + self._term_count[current_order] = {} + self._term_count[current_order]["total"] = 0 if not df.empty: uvals, ucnts = np.unique(df["term_index"], return_counts=True) for uval, cnt in zip(uvals, ucnts): - if uval not in self._term_count[ord]: - self._term_count[ord][uval] = cnt + if uval not in self._term_count[current_order]: + self._term_count[current_order][uval] = cnt else: - self._term_count[ord][uval] += cnt + self._term_count[current_order][uval] += cnt - self._term_count[ord]["total"] += cnt + self._term_count[current_order]["total"] += cnt return True From 9a99ee4d51029a7a9cc85ebbde06b0d6e2d68eec Mon Sep 17 00:00:00 2001 From: "Jessica A. Nash" Date: Fri, 7 Sep 2018 11:06:05 -0400 Subject: [PATCH 6/8] amber: work on dl compatibility check --- eex/translators/amber/amber_write.py | 56 ++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/eex/translators/amber/amber_write.py b/eex/translators/amber/amber_write.py index 90665f5..65f0d89 100644 --- a/eex/translators/amber/amber_write.py +++ b/eex/translators/amber/amber_write.py @@ -9,6 +9,7 @@ import re import numpy as np from collections import Counter +from copy import deepcopy # Python 2/3 compat try: @@ -26,7 +27,6 @@ def _write_1d(file_handle, data, ncols, fmt): - data = data.ravel() remainder_size = data.size % ncols @@ -90,16 +90,63 @@ def _check_dl_compatibility(dl): # Loop over force field information - check functional form compatibility for k, v in amd.forcefield_parameters.items(): if k is not "nonbond": - terms = dl.list_term_parameters(v["order"]) + terms = deepcopy(dl.list_term_parameters(v["order"])) - for j in terms.values(): + for id, j in terms.items(): term_md = eex.metadata.get_term_metadata(v["order"], "forms", j[0]) canonical_form = term_md['canonical_form'] compatible_forms = eex.metadata.get_term_metadata(v["order"], "group")[canonical_form] if v['form'] not in compatible_forms: - # Will need to insert check to see if these can be easily converted (ex OPLS dihedral <-> charmmfsw) raise TypeError("Functional form %s stored in datalayer is not compatible with Amber.\n" % (j[0])) + + elif j[0] != v['form']: + # Functional form is compatible, but not the correct form. Perform conversion. + + # Grab here since multiple conversions will change indices. + term_list = dl.get_terms(v["order"]) + + # Need to get parameters in dictionary for conversion + term_parameters = eex.metadata.get_term_metadata(v["order"], "forms", j[0])["parameters"] + convert_parameters = {k: v for k, v in zip(term_parameters, j[1:])} + + # Call conversion function + new_form = eex.form_converters.convert_form(v["order"], convert_parameters, j[0], v['form']) + + # Get info for original functional form to remove. + remove_terms = term_list[term_list["term_index"] == id] + atom_columns = [x for x in remove_terms.columns if "atom" in x] + + # Remove terms for original functional form + dl.remove_terms(v['order'], index=remove_terms.index, propogate=False) + + # Add new - Two things must be done here: + # First the converted functional form must be added using dl.add_term_parameter + # Then, terms themselves must be re-added using dl.add_term + + # Build dataframe + # 1. Get atoms involved in term from remove terms - 'base_atoms' + # 2. Build df with atoms and term_index + + base_atoms = remove_terms[atom_columns] + new_form_keys = new_form.keys() + + # Loop through new terms to add to dl + for val in range(len(list(new_form.values())[0])): + to_add = {} + + for key in new_form_keys: + to_add[key] = new_form[key][val] + + + uid = dl.add_term_parameter(v["order"], v['form'], to_add) + base_atoms['term_index'] = uid + + dl.add_terms(v["order"], base_atoms) + + # Remove this form from dl + #print(dl.get_terms(v["order"])) + else: # Handle nonbonds. Make sure only LJ types are stored in datalayer. nb_forms = dl.list_stored_nb_types() @@ -235,6 +282,7 @@ def write_amber_file(dl, filename, inpcrd=None): # First get information into Amber pointers. All keys are initially filled with zero. # Ones that are currently 0, but should be implemented eventually are marked with + # Check that datalayer is compatible with amber _check_dl_compatibility(dl) dihedral_count = _get_charmm_dihedral_count(dl) From 4fc7789680c76650339f0442001cef0daaad25cd Mon Sep 17 00:00:00 2001 From: "Jessica A. Nash" Date: Fri, 7 Sep 2018 11:13:58 -0400 Subject: [PATCH 7/8] add metadata import to __init__ --- eex/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/eex/__init__.py b/eex/__init__.py index dbdcd05..a24c178 100644 --- a/eex/__init__.py +++ b/eex/__init__.py @@ -10,6 +10,7 @@ from . import translators from . import utility from . import form_converters +from . import metadata from .units import ureg From 3ff92c1bc6082192a81a86554c5b310c898e5e8a Mon Sep 17 00:00:00 2001 From: "Jessica A. Nash" Date: Fri, 7 Sep 2018 11:14:18 -0400 Subject: [PATCH 8/8] tests: add tests for amber reader --- eex/tests/conftest.py | 9 +++++++ eex/tests/scratch/test_output | 45 +++++++++++++------------------ eex/tests/test_amber.py | 15 +++++++++++ eex/tests/test_compare_alkanes.py | 2 +- 4 files changed, 43 insertions(+), 28 deletions(-) diff --git a/eex/tests/conftest.py b/eex/tests/conftest.py index f4eedad..f8fdca6 100644 --- a/eex/tests/conftest.py +++ b/eex/tests/conftest.py @@ -71,14 +71,23 @@ def build_butane(ff=True, nb=True, scale=True): dl.add_dihedrals(dihedral_df) if ff: + # Add harmonic bond dl.add_term_parameter(3, "harmonic", {'K': 62.100, 'theta0': 114}, uid=0, utype={'K': 'kcal * mol ** -1 * radian ** -2', 'theta0': 'degree'}) + # Add harmonic angle dl.add_term_parameter(2, "harmonic", {'K': 300.9, 'R0': 1.540}, uid=0, utype={'K': "kcal * mol **-1 * angstrom ** -2", 'R0': "angstrom"}) + # Add opls dihedral + dl.add_term_parameter(4, "opls", {'K_1': 1.41103414, 'K_2': -0.27101489, + 'K_3': 3.14502869, 'K_4': 0}, uid=0, utype={'K_1': 'kcal * mol ** -1', + 'K_2': 'kcal * mol ** -1', + 'K_3': 'kcal * mol ** -1', + 'K_4': 'kcal * mol ** -1'}) + if nb: dl.add_nb_parameter(atom_type=1, nb_name="LJ", nb_model="epsilon/sigma", nb_parameters={'sigma': 3.75, 'epsilon': 0.1947460018}, diff --git a/eex/tests/scratch/test_output b/eex/tests/scratch/test_output index ba779e6..3ddd07b 100644 --- a/eex/tests/scratch/test_output +++ b/eex/tests/scratch/test_output @@ -3,56 +3,50 @@ LAMMPS data file generated by MolSSI EEX 4 atoms 2 atom types 3 bonds - 2 bond types + 1 bond types 2 angles 1 angle types - 4 dihedrals - 4 dihedral types + 1 dihedrals + 1 dihedral types 0 impropers 0 improper types - 0.000000 100.000000 xlo xhi - 0.000000 100.000000 ylo yhi - 0.000000 100.000000 zlo zhi +-47.690000 52.310000 xlo xhi +-50.000000 50.000000 ylo yhi +-50.000000 50.000000 zlo zhi +Pair Coeffs -PairIJ Coeffs - - 1 1 0.19474595 3.75000000 - 1 2 0.13342411 3.85000000 - 2 2 0.09141137 3.95000000 + 1 0.09141129 3.95000000 + 2 0.19474600 3.75000000 Bond Coeffs 1 300.90000000 1.54000000 - 2 300.90000000 1.54000000 Angle Coeffs - 1 62.10011250 114.00004886 + 1 62.10011249 114.00000000 Dihedral Coeffs - 1 0.00000000 0.00000000 0.00000000 - 2 0.70551689 1.00000000 0.00000000 - 3 -0.13550741 2.00000000 -180.00007714 - 4 1.57251395 3.00000000 0.00000000 + 1 1.41103414 -0.27101489 3.14502869 0.00000000 Masses -1 15.0452 -2 14.02658 +2 15.03452 +1 14.02658 Atoms -1 1 1 0.00000000 0.00000000 -0.45970000 -1.53020000 -2 1 2 0.00000000 0.00000000 0.00000000 0.00000000 -3 1 2 0.00000000 0.00000000 1.59800000 0.00000000 -4 1 1 0.00000000 -1.47400000 1.57300000 -0.61670000 +1 1 2 0.00000000 0.00000000 -0.45970000 -1.53020000 +2 1 1 0.00000000 0.00000000 0.00000000 0.00000000 +3 1 1 0.00000000 0.00000000 1.59800000 0.00000000 +4 1 2 0.00000000 -1.47400000 1.57300000 -0.61670000 Bonds 1 1 1 2 -2 2 2 3 +2 1 2 3 3 1 3 4 Angles @@ -63,7 +57,4 @@ Dihedral Coeffs Dihedrals 1 1 1 2 3 4 -2 2 1 2 3 4 -3 3 1 2 3 4 -4 4 1 2 3 4 diff --git a/eex/tests/test_amber.py b/eex/tests/test_amber.py index 4b14068..ff5890f 100644 --- a/eex/tests/test_amber.py +++ b/eex/tests/test_amber.py @@ -206,6 +206,21 @@ def test_amber_compatibility_no_scaling(butane_dl): eex.translators.amber.write_amber_file(dl, oname) +def test_amber_compatibility_dihedral_conversion(butane_dl): + + dl = butane_dl() + + oname = eex_find_files.get_scratch_directory("dl_compatibility.prmtop") + + eex.translators.amber.amber_write._check_dl_compatibility(dl) + + print(dl.get_term_count(4)) + + print(dl.get_terms(4)) + + # eex.translators.amber.write_amber_file(dl, oname) + + diff --git a/eex/tests/test_compare_alkanes.py b/eex/tests/test_compare_alkanes.py index f078e89..0d16d7a 100644 --- a/eex/tests/test_compare_alkanes.py +++ b/eex/tests/test_compare_alkanes.py @@ -74,7 +74,7 @@ def test_alkane(lammps_bench, program): # write test that loads in amber --> lammps --> EEX compare datalayers -@pytest.mark.parametrize("program1", ["amber"]) #build dl +@pytest.mark.parametrize("program1", ["amber", "lammps"]) #build dl @pytest.mark.parametrize("program2", ["amber", "lammps"]) #write dl @pytest.mark.parametrize("molecule", _alkane_molecules) def test_translation(program1, program2, molecule):