diff --git a/pycgtool/bondset.py b/pycgtool/bondset.py index 4cf7892b8eb5ceecb20c188c16f421412cb05a30..e4faef5912b5dc949d8cfddf3519b3982bcbd90e 100644 --- a/pycgtool/bondset.py +++ b/pycgtool/bondset.py @@ -135,12 +135,12 @@ class BondSet: pass with CFG(filename) as cfg: - for mol in cfg: - self._molecules[mol.name] = [] - mol_bonds = self._molecules[mol.name] + for mol_name, mol_section in cfg.items(): + self._molecules[mol_name] = [] + mol_bonds = self._molecules[mol_name] angles_defined = False - for atomlist in mol: + for atomlist in mol_section: try: # TODO consider best way to override default func form # On per bond, or per type basis diff --git a/pycgtool/forcefield.py b/pycgtool/forcefield.py index d9d4a9bba61158d5a07695b2678ef125fc38076a..26d8a0f2d74f9bbbb3c02d3e8e44f9999a948b9b 100644 --- a/pycgtool/forcefield.py +++ b/pycgtool/forcefield.py @@ -7,7 +7,7 @@ import shutil import functools from .util import dir_up -from .parsers.cfg import CFG +from .parsers import ITP class ForceField: @@ -29,8 +29,8 @@ class ForceField: dist_dat_dir = os.path.join(dir_up(os.path.realpath(__file__), 2), "data") # Copy main MARTINI itp - shutil.copyfile(os.path.join(dist_dat_dir, "martini_v2.2.itp"), - os.path.join(self.dirname, "martini_v2.2.itp")) + martini_itp = os.path.join(dist_dat_dir, "martini_v2.2.itp") + shutil.copyfile(martini_itp, os.path.join(self.dirname, "martini_v2.2.itp")) # Copy water models shutil.copyfile(os.path.join(dist_dat_dir, "watermodels.dat"), os.path.join(self.dirname, "watermodels.dat")) @@ -38,9 +38,9 @@ class ForceField: os.path.join(self.dirname, "w.itp")) # Create atomtypes.atp required for correct masses with pdb2gmx - with CFG(os.path.join(dist_dat_dir, "martini_v2.2.itp"), allow_duplicate=True) as cfg,\ - open(os.path.join(self.dirname, "atomtypes.atp"), 'w') as atomtypes: - for toks in cfg["atomtypes"]: + atomtypes_atp = os.path.join(self.dirname, "atomtypes.atp") + with ITP(martini_itp) as itp, open(atomtypes_atp, 'w') as atomtypes: + for toks in itp["atomtypes"]: print(" ".join(toks), file=atomtypes) with open(os.path.join(self.dirname, "forcefield.doc"), "w") as doc: diff --git a/pycgtool/mapping.py b/pycgtool/mapping.py index b42b88f16d0236e2cd95ea5e9a71560749dc8f48..e566c45e7756913bde28eef24edb80636caa55be 100644 --- a/pycgtool/mapping.py +++ b/pycgtool/mapping.py @@ -88,16 +88,16 @@ class Mapping: with CFG(filename) as cfg: self._manual_charges = {} - for mol in cfg: - self._mappings[mol.name] = [] - self._manual_charges[mol.name] = False - molmap = self._mappings[mol.name] - for name, typ, first, *atoms in mol: + for mol_name, mol_section in cfg.items(): + self._mappings[mol_name] = [] + self._manual_charges[mol_name] = False + molmap = self._mappings[mol_name] + for name, typ, first, *atoms in mol_section: charge = 0 try: # Allow optional charge in mapping file charge = float(first) - self._manual_charges[mol.name] = True + self._manual_charges[mol_name] = True except ValueError: atoms.insert(0, first) assert atoms, "Bead {0} specification contains no atoms".format(name) diff --git a/pycgtool/parsers/__init__.py b/pycgtool/parsers/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e0e5097c46606ed931eb98d063f45da45c57c9c9 100644 --- a/pycgtool/parsers/__init__.py +++ b/pycgtool/parsers/__init__.py @@ -0,0 +1,3 @@ +from .cfg import CFG + +ITP = CFG \ No newline at end of file diff --git a/pycgtool/parsers/cfg.py b/pycgtool/parsers/cfg.py index 87695b346b5c556780aea3594a7bb201db60d420..1edb079c97a28098a43d86f504c90001a4c83b6b 100644 --- a/pycgtool/parsers/cfg.py +++ b/pycgtool/parsers/cfg.py @@ -5,35 +5,7 @@ Format is based upon GROMACS .itp files but does not support nesting of sections """ import os -from collections import OrderedDict - - -class Section: - """ - Class representing a single section of the config file. - """ - __slots__ = ["name", "_lines"] - - def __init__(self, name=None): - """ - Create a section and storage for the lines it contains. - - :param name: Name of section - """ - self.name = name - self._lines = [] - - def __len__(self): - return len(self._lines) - - def __iter__(self): - return iter(self._lines) - - def __getitem__(self, item): - return self._lines[item] - - def add_line(self, line): - self._lines.append(line) +import collections class DuplicateSectionError(Exception): @@ -44,24 +16,23 @@ class DuplicateSectionError(Exception): return "Section {0} appears twice in file {1}.".format(*self.args) -class CFG: +class CFG(collections.OrderedDict): """ Class representing a CFG file. Contains a dictionary of Sections. """ - __slots__ = ["filename", "_sections"] + __slots__ = ["filename"] - def __init__(self, filename=None, allow_duplicate=False): + def __init__(self, filename=None): """ Parse a config file and extract Sections. :param filename: Name of file to read - :param allow_duplicate: Allow sections to appear more than once in a file :return: Instance of CFG """ + super(CFG, self).__init__() self.filename = filename - self._sections = OrderedDict() with open(self.filename) as f: curr_section = None @@ -73,33 +44,21 @@ class CFG: elif line.startswith("#include"): cfg2 = CFG(os.path.join(os.path.dirname(self.filename), line.split()[1])) - self._sections.update(cfg2._sections) + self.update(cfg2) continue elif line.startswith("["): curr_section = line.strip("[ ]") - if curr_section in self._sections and not allow_duplicate: + if curr_section in self: raise DuplicateSectionError(curr_section, self.filename) - self._sections[curr_section] = Section(name=curr_section) + self[curr_section] = [] continue toks = tuple(line.split()) - self._sections[curr_section].add_line(toks) + self[curr_section].append(toks) def __enter__(self): return self def __exit__(self, type, value, traceback): pass - - def __len__(self): - return len(self._sections) - - def __iter__(self): - return iter(self._sections.values()) - - def __contains__(self, item): - return item in self._sections - - def __getitem__(self, item): - return self._sections[item] diff --git a/test/test_parsers_cfg.py b/test/test_parsers_cfg.py index db6d9a482fc3c975eda4130618f24a15cfe1e3ec..442d818b10cf23e1f435f7176c155ea18d934254 100644 --- a/test/test_parsers_cfg.py +++ b/test/test_parsers_cfg.py @@ -1,6 +1,7 @@ import unittest -from pycgtool.parsers.cfg import CFG, DuplicateSectionError +from pycgtool.parsers.cfg import DuplicateSectionError +from pycgtool.parsers import CFG class TestParsersCFG(unittest.TestCase): @@ -12,13 +13,13 @@ class TestParsersCFG(unittest.TestCase): def test_cfg_iterate_sections(self): with CFG("test/data/water.map") as cfg: - for section in cfg: - self.assertEqual("SOL", section.name) + for name in cfg: + self.assertEqual("SOL", name) def test_cfg_iterate_lines(self): with CFG("test/data/water.map") as cfg: - for section in cfg: - self.assertEqual("SOL", section.name) + for name, section in cfg.items(): + self.assertEqual("SOL", name) for line in section: self.assertEqual(self.watline, line)