def test_relabel_interaction(atom_defs):
    interaction = Interaction(atom_defs, ["1", "ga_2"], {"test": "value"})
    mapping = {1: "A", 2: "B", 3: "C"}
    new_interaction = _relabel_interaction_atoms(interaction, mapping)
    assert new_interaction.atoms == ["A", "B", "C"]
    assert new_interaction.parameters == ["1", "ga_2"]
    assert new_interaction.meta == {"test": "value"}
def test_do_mapping_mods(modified_molecule, modifications):
    """
    Test do_mapping on a molecule with modifications
    """
    mappings = {}
    for name in 'ABCDEFGHIJ':
        block = Block(force_field=FF_UNIVERSAL, name=name)
        block.add_node(name, atomname=name, resid=1)
        mappings[name] = Mapping(block, block, mapping={name: {name: 1}},
                                 references={}, ff_from=FF_UNIVERSAL,
                                 ff_to=FF_UNIVERSAL, names=(name,))
    for mod in modifications.values():
        name = (mod.name,) if isinstance(mod.name, str) else mod.name
        mappings[name] = Mapping(mod, mod, mapping={idx: {idx: 1} for idx in mod},
                                 references={}, ff_from=FF_UNIVERSAL,
                                 ff_to=FF_UNIVERSAL, names=name, type='modification')
    mappings = {'universal': {'universal': mappings}}

    out = do_mapping(modified_molecule, mappings, FF_UNIVERSAL,
                     attribute_keep=('chain', 'resid'))
    pprint.pprint(list(out.nodes(data=True)))
    pprint.pprint(list(out.edges))

    expected = modified_molecule.copy()
    for node in expected.nodes:
        expected.nodes[node]['mapping_weights'] = {node: 1}
        expected.nodes[node]['graph'] = expected.subgraph([node])

    expected = nx.relabel_nodes(expected, {idx: idx+1 for idx in expected})
    pprint.pprint(list(expected.nodes(data='atomname')))
    pprint.pprint(list(expected.edges))

    assert equal_graphs(expected, out, node_attrs=['atomname', 'resid', 'mapping_weights'])
    assert out.interactions['bond'] == [Interaction(atoms=(10, 11), parameters=(3, 4), meta={})]
Exemple #3
0
def test_IOError():
    """
    Tests if VS1 with function type 1 raises error.
    """
    with pytest.raises(IOError):
        construct_vs("virtual_sites4", Interaction(
            atoms=[], parameters=["1"], meta={}), {})
Exemple #4
0
def expand_excl(molecule):
    """
    Given a `molecule` add exclusions for nodes that
    have the exclude attribute.

    Parameters:
    -----------
    molecule: `:class:vermouth.molecule`

    Returns:
    --------
    `:class:vermouth.molecule`
    """
    exclude = nx.get_node_attributes(molecule, "exclude")
    nrexcl = molecule.nrexcl
    had_excl = []
    for node, excl in exclude.items():
        if excl > nrexcl:
            excluded_nodes = neighborhood(molecule,
                                          node,
                                          max_length=excl,
                                          min_length=nrexcl)
            for ndx in excluded_nodes:
                excl = Interaction(atoms=[node, ndx], parameters=[], meta={})
                if frozenset([node, ndx]) not in had_excl and node != ndx:
                    had_excl.append(frozenset([node, ndx]))
                    molecule.interactions["exclusions"].append(excl)
    return molecule
    def test_multiresidue_block():
        lines = """
        [ moleculetype ]
        ; name nexcl.
        PEO         1
        ;
        [ atoms ]
        1  SN1a    1   PEO   CO1  1   0.000  45
        [ moleculetype ]
        ; name nexcl.
        MIX         1
        ;
        [ atoms ]
        1  SN1a    1   R1   C1  1   0.000  45
        2  SN1a    1   R1   C2  1   0.000  45
        3  SC1     2   R2   C1  2   0.000  45
        4  SC1     2   R2   C2  2   0.000  45
        [ bonds ]
        ; back bone bonds
        1  2   1   0.37 7000
        2  3   1   0.37 7000
        3  4   1   0.37 7000
        """
        lines = textwrap.dedent(lines).splitlines()
        ff = vermouth.forcefield.ForceField(name='test_ff')
        polyply.src.polyply_parser.read_polyply(lines, ff)
        meta_mol = MetaMolecule(name="test", force_field=ff)
        meta_mol.add_monomer(0,"PEO",[])
        meta_mol.add_monomer(1,"MIX",[(1,0)])

        new_meta_mol = polyply.src.map_to_molecule.MapToMolecule().run_molecule(meta_mol)

        bonds = [Interaction(atoms=(1, 2), parameters=['1', '0.37', '7000'], meta={}),
                 Interaction(atoms=(2, 3), parameters=['1', '0.37', '7000'], meta={}),
                 Interaction(atoms=(3, 4), parameters=['1', '0.37', '7000'], meta={})]

        edges = [(0,1), (1,2)]

        assert new_meta_mol.molecule.interactions['bonds'] == bonds
        assert len(new_meta_mol.nodes) == 3
        assert list(new_meta_mol.edges) == edges
        assert nx.get_node_attributes(new_meta_mol, "resname") == {0: "PEO", 1: "R1", 2: "R2"}
    def test_add_blocks():
        lines = """
        [ moleculetype ]
        ; name nexcl.
        PEO         1
        ;
        [ atoms ]
        1  SN1a    1   PEO   CO1  1   0.000  45
        2  SN1a    1   PEO   CO2  1   0.000  45
        3  SN1a    1   PEO   CO3  1   0.000  45
        4  SN1a    1   PEO   CO4  1   0.000  45
        [ bonds ]
        ; back bone bonds
        1  2   1   0.37 7000
        2  3   1   0.37 7000
        2  4   1   0.37 7000
        4  5   1   0.37 7000
        """
        lines = textwrap.dedent(lines).splitlines()
        ff = vermouth.forcefield.ForceField(name='test_ff')
        polyply.src.polyply_parser.read_polyply(lines, ff)
        meta_mol = MetaMolecule(name="test", force_field=ff)
        meta_mol.add_monomer(0,"PEO",[])
        meta_mol.add_monomer(1,"PEO",[(1,0)])
        new_meta_mol = polyply.src.map_to_molecule.MapToMolecule().run_molecule(meta_mol)

        bonds = [Interaction(atoms=(0, 1), parameters=['1', '0.37', '7000'], meta={}),
                 Interaction(atoms=(1, 2), parameters=['1', '0.37', '7000'], meta={}),
                 Interaction(atoms=(1, 3), parameters=['1', '0.37', '7000'], meta={}),
                 Interaction(atoms=(4, 5), parameters=['1', '0.37', '7000'], meta={}),
                 Interaction(atoms=(5, 6), parameters=['1', '0.37', '7000'], meta={}),
                 Interaction(atoms=(5, 7), parameters=['1', '0.37', '7000'], meta={})]

        assert new_meta_mol.molecule.interactions['bonds'] == bonds
def test_sort_molecule_atoms():
    """
    Test the :class:`vermouth.processors.sort_molecule_atoms.SortMoleculeAtoms`
    processor in normal conditions.
    """
    molecule = Molecule()
    nodes = [
        (6, {'chain': 'C', 'resid': 2, 'resname': 'AA0', 'atomname': 'A5'}),
        (5, {'chain': 'A', 'resid': 2, 'resname': 'XX0', 'atomname': 'A0'}),
        (4, {'chain': 'B', 'resid': 1, 'resname': 'XX1', 'atomname': 'A2'}),
        (3, {'chain': 'C', 'resid': 1, 'resname': 'XX0', 'atomname': 'A4'}),
        (2, {'chain': 'C', 'resid': 2, 'resname': 'AA1', 'atomname': 'A6'}),
        (1, {'chain': 'A', 'resid': 2, 'resname': 'XX1', 'atomname': 'A1'}),
        (0, {'chain': 'B', 'resid': 2, 'resname': 'XX0', 'atomname': 'A3'}),
    ]
    edges = [
        (0, 6, {'content': 'something'}),
        (1, 2, {'hello': 34}),
    ]
    interactions = {
        'bonds': [
            Interaction(atoms=[0, 1], parameters=['a', 'b'], meta={'a': 7}),
            Interaction(atoms=[3, 4], parameters=['c', 'd'], meta={'b': 5}),
        ],
        'angles': [
            Interaction(atoms=[0, 1, 2], parameters=['b', 'c'], meta={'a': 0}),
        ],
    }

    molecule.add_nodes_from(nodes)
    molecule.add_edges_from(edges)
    molecule.interactions = copy.copy(interactions)

    processor = SortMoleculeAtoms()
    processor.run_molecule(molecule)

    assert list(molecule.nodes) == [5, 1, 4, 0, 3, 6, 2]
    assert sorted(molecule.edges(data=True)) == edges
    assert molecule.interactions == interactions
Exemple #8
0
def test_to_molecule():
    """
    Test if the to molecule function gives
    expected results.
    """
    test_block = vermouth.molecule.Block()
    test_block.add_edges_from([('A', 'B'), ('B', 'C')])
    test_block.interactions["bonds"] = [
                Interaction(atoms=('A', 'B'),
                            parameters=['a', '0.2', '200'],
                            meta={'a': 0}),
                Interaction(atoms=('B', 'C'),
                            parameters=['a', '0.1', '300'],
                            meta={'b': 1}),]

    test_molecule = test_block.to_molecule()

    ref_bonds = [Interaction(atoms=(0, 1),
                             parameters=['a', '0.2', '200'],
                             meta={'a': 0}),
                 Interaction(atoms=(1, 2),
                             parameters=['a', '0.1', '300'],
                             meta={'b': 1}),]
    assert ref_bonds == test_molecule.interactions['bonds']
    def _base_parser(self, tokens, context, section, atom_idxs):
        """
        Converts an interaction line into a vermouth interaction
        tuple. It updates the block interactions in place.

        Parameters
        ----------
        tokens: collections.deque[str]
            Deque of token to inspect. The deque **can be modified** in place.
        context: :class:`vermouth.molecule.Block`
            The current block we parse
        section: str
            The current section header
        atom_idxs: list of ints or strings that are valid python slices
        """
        # split atoms and parameters

        atoms, parameters = self._split_atoms_and_parameters(tokens, atom_idxs)

        # perform check on the atom ids
        treated_atoms = self._treat_block_interaction_atoms(
            atoms, context, section)

        if self.current_meta:
            meta = {self.current_meta['condition']: self.current_meta['tag']}
        else:
            meta = {
            }  #dict(collections.ChainMap(meta, apply_to_all_interactions))

        interaction = Interaction(
            atoms=treated_atoms,
            parameters=parameters,
            meta=meta,
        )

        context.interactions[section] = context.interactions.get(
            section, []) + [interaction]
Exemple #10
0
        subgraph.edges[edge[0], edge[1]]
        for edge in sorted_expected
    ]
    expected_attributes = [
        edges_between_molecule.edges[edge[0], edge[1]]
        for edge in sorted_expected
    ]
    assert found_attributes == expected_attributes


@pytest.mark.parametrize('left, right, expected', (
    (  # Same
        {
            'bonds': [
                Interaction(atoms=('A', 'B'),
                            parameters=['a', '0.2', '200'],
                            meta={'a': 0}),
                Interaction(atoms=('B', 'C'),
                            parameters=['a', '0.1', '300'],
                            meta={'b': 1}),
            ],
            'angles': [
                Interaction(atoms=('A', 'B', 'C'),
                            parameters=['1', '0.2', '200'],
                            meta={'a': 0}),
            ],
        },
        {
            'bonds': [
                Interaction(atoms=('A', 'B'),
                            parameters=['a', '0.2', '200'],
Exemple #11
0
# limitations under the License.
"""
Test constructions of virutal-sites. Reference values come from
a GROMACS energy minimization.
"""

import numpy as np
import pytest
from vermouth.molecule import Interaction
from ..src.virtual_site_builder import construct_vs


@pytest.mark.parametrize('vs_type, interaction, positions, result', (
    # vsn
    ("virtual_sitesn",
     Interaction(atoms=[6, 1, 2, 3, 4, 5], parameters=["1"], meta={}),
     {1: np.array([4.924, 0.353, 0.036]), 2: np.array([5.191, 0.023, 0.208]),
      3: np.array([5.190, -0.231, -0.162]), 4: np.array([4.734, -0.268, -0.131]),
      5: np.array([4.548, 0.088, 0.046])},
     np.array([4.917, -0.007, -0.001])
     ),
    #  vs2
    ("virtual_sites2",
     Interaction(atoms=[3, 1, 2], parameters=["1", "0.5"], meta={}),
     {1: np.array([0.000, 0.000, 0.000]), 2: np.array([-0.012, 0.002, 0.024])},
     np.array([-0.006, 0.001, 0.012])
     ),
    #  vs31
    ("virtual_sites3",
     Interaction(atoms=[4, 1, 2, 3], parameters=[
                 "1", "0.223", "0.865"], meta={}),
class TestGenTemps:

      @staticmethod
      def test_find_atoms():
          G = nx.Graph()
          G.add_edges_from([(1, 2), (2, 3), (3, 4)])
          attrs = {1: {"resname": "test", "id": 'A'},
                   2: {"resname": "test", "id": 'B'},
                   3: {"resname": "testB", "id": 'A'},
                   4: {"resname": "testB", "id": 'B'}}
          nx.set_node_attributes(G, attrs)
          nodes = find_atoms(G, "resname", "test")
          assert nodes == [1, 2]
          nodes = find_atoms(G, "id", "A")
          assert nodes == [1, 3]

      @staticmethod
      def test_expand_inital_coords():
        lines = """
        [ moleculetype ]
        GLY 1

        [ atoms ]
        1 P4 1 ALA BB 1
        2 P3 1 ALA SC1 2
        3 P2 1 ALA SC2 3
        4 VS 1 ALA SC3 3

        [ bonds ]
        1 2 1 0.2 100
        2 3 1 0.6 700

        [ virtual_sitesn ]
        4 2 3 1
        """
        lines = textwrap.dedent(lines).splitlines()
        ff = vermouth.forcefield.ForceField(name='test_ff')
        polyply.src.polyply_parser.read_polyply(lines, ff)
        block = ff.blocks['GLY']
        coords = _expand_inital_coords(block)
        assert len(coords) == 4
        for pos in coords.values():
            assert len(pos) == 3

      @staticmethod
      def test_compute_volume():
          lines = """
          [ moleculetype ]
          GLY 1

          [ atoms ]
          1 P1 1 ALA BB 1
          2 P1 1 ALA SC1 2
          3 P1 1 ALA SC2 3
          4 P1 1 ALA SC3 3

          [ bonds ]
          1 2 1 0.2 100
          2 3 1 0.6 700
          3 4 1 0.2 700
          """
          meta_mol = polyply.MetaMolecule()
          nonbond_params = {frozenset(["P1", "P1"]): {"nb1": 0.47, "nb2":0.5}}

          lines = textwrap.dedent(lines).splitlines()
          ff = vermouth.forcefield.ForceField(name='test_ff')
          polyply.src.polyply_parser.read_polyply(lines, ff)
          block = ff.blocks['GLY']
          coords = _expand_inital_coords(block)
          vol = compute_volume(meta_mol, block, coords, nonbond_params)
          assert vol > 0.

      @staticmethod
      def test_map_from_CoG():
         lines = """
         [ moleculetype ]
         GLY 1
         [ atoms ]
         1 P4 1 ALA BB 1
         2 P3 1 ALA SC1 2
         3 P2 1 ALA SC2 3
         4 P2 1 ALA SC3 3
         [ bonds ]
         1 2 1 0.2 100
         2 3 1 0.6 700
         3 4 1 0.2 700
         """
         lines = textwrap.dedent(lines).splitlines()
         ff = vermouth.forcefield.ForceField(name='test_ff')
         polyply.src.polyply_parser.read_polyply(lines, ff)
         block = ff.blocks['GLY']
         coords = _expand_inital_coords(block)
         points = np.array(list(coords.values()))
         CoG = center_of_geometry(points)
         new_coords = map_from_CoG(coords)
         for coord in coords:
             coords[coord] == new_coords[coord] + CoG

      @staticmethod
      @pytest.mark.parametrize('atom_defs', (
      [1, 2, 3],
      (1, 2, 3)))
      def test_relabel_interaction(atom_defs):
         interaction = Interaction(atom_defs, ["1", "ga_2"], {"test": "value"})
         mapping = {1: "A", 2: "B", 3: "C"}
         new_interaction = _relabel_interaction_atoms(interaction, mapping)
         assert new_interaction.atoms == ["A", "B", "C"]
         assert new_interaction.parameters == ["1", "ga_2"]
         assert new_interaction.meta == {"test": "value"}

      @staticmethod
      def test_extract_block():
         lines = """
         [ moleculetype ]
         test 1
         [ atoms ]
         1 P4 1 GLY BB 1
         2 P3 1 GLY SC1 2
         3 P2 1 ALA SC2 3
         4 P2 1 ALA SC3 3
         [ bonds ]
         1 2 1 0.2 100
         2 3 1 0.6 700
         3 4 1 0.2 700
         [ moleculetype ]
         GLY 1
         [ atoms ]
         1 P4 1 GLY BB 1
         2 P3 1 GLY SC1 2
         [ bonds ]
         1 2 1 0.2 100
         """
         lines = textwrap.dedent(lines).splitlines()
         ff = vermouth.forcefield.ForceField(name='test_ff')
         polyply.src.polyply_parser.read_polyply(lines, ff)
         block = ff.blocks['test']
         molecule = block.to_molecule()
         new_block = extract_block(molecule, "GLY", {})
         for node in ff.blocks["GLY"]:
             atomname = ff.blocks["GLY"].nodes[node]["atomname"]
             assert ff.blocks["GLY"].nodes[node] == new_block.nodes[atomname]
         for inter_type in ff.blocks["GLY"].interactions:
             len(ff.blocks["GLY"].interactions[inter_type]) == len(new_block.interactions[inter_type])

      @staticmethod
      def test_run_molecule():
          top = polyply.src.topology.Topology.from_gmx_topfile(TEST_DATA + "/topology_test/system.top", "test")
          top.gen_pairs()
          top.convert_nonbond_to_sig_eps()
          GenerateTemplates(topology=top, max_opt=10).run_molecule(top.molecules[0])
          assert "PMMA" in top.volumes
          assert "PMMA" in top.molecules[0].templates

      @staticmethod
      @pytest.mark.parametrize('lines, result', (
        ("""
         [ moleculetype ]
         test 1
         [ atoms ]
         1 P4 1 GLY BB 1
         2 P3 1 GLY SC1 2
         3 P2 1 ALA SC2 3
         [ bonds ]
         1 2 1 0.2 100
         2 3 1 0.2 100
         """,
         (False, Interaction(atoms=["1", "2"], parameters=["1", "0.2", "100"], meta={}),
          "bonds")),
        ("""
         [ moleculetype ]
         test 1
         [ atoms ]
         1 P4 1 GLY BB 1
         2 P3 1 GLY SC1 2
         3 VS 1 ALA SC2 3
         4 P3 1 ALA SC3 3
         [ virtual_sitesn ]
         3 1 4 2 1
         """,
         (True, Interaction(atoms=["3", "4", "2", "1"], parameters=["1"], meta={}),
          "virtual_sitesn"))))
      def test_find_interaction_involving(lines, result):
          lines = textwrap.dedent(lines).splitlines()
          ff = vermouth.forcefield.ForceField(name='test_ff')
          polyply.src.polyply_parser.read_polyply(lines, ff)
          block = ff.blocks['test']
          result = find_interaction_involving(block, 1, 2)
          assert result == result

      @staticmethod
      @pytest.mark.parametrize('lines', (
        """
         [ moleculetype ]
         test 1
         [ atoms ]
         1 P4 1 GLY BB 1
         2 P3 1 GLY SC1 2
         3 P2 1 ALA SC2 3
         [ bonds ]
         1 2 1 0.2 100
         """,
        """
         [ moleculetype ]
         test 1
         [ atoms ]
         1 P4 1 GLY BB 1
         2 P3 1 GLY SC1 2
         3 VS 1 ALA SC2 3
         4 P3 1 ALA SC3 3
         """))
      def test_find_interaction_involving_error(lines):
          lines = textwrap.dedent(lines).splitlines()
          ff = vermouth.forcefield.ForceField(name='test_ff')
          polyply.src.polyply_parser.read_polyply(lines, ff)
          block = ff.blocks['test']
          with pytest.raises(IOError):
               find_interaction_involving(block, 1, 2)
Exemple #13
0
class TestTopology:
    @staticmethod
    def test_from_gmx_topfile():
        top = Topology.from_gmx_topfile(
            TEST_DATA + "/topology_test/system.top", "test")
        assert len(top.molecules) == 1

    @staticmethod
    def test_add_positions_from_gro():
        top = Topology.from_gmx_topfile(
            TEST_DATA + "/topology_test/system.top", "test")
        top.add_positions_from_file(TEST_DATA + "/topology_test/test.gro")
        for node in top.molecules[0].molecule.nodes:
            if node < 14:
                assert "position" in top.molecules[0].molecule.nodes[
                    node].keys()

        for node in top.molecules[0].nodes:
            if node != 2:
                assert "position" in top.molecules[0].nodes[node].keys()
                assert top.molecules[0].nodes[node]["build"] == False
            else:
                assert top.molecules[0].nodes[node]["build"] == True

    @staticmethod
    def test_add_positions_from_pdb():
        top = Topology.from_gmx_topfile(TEST_DATA + "/topology_test/pdb.top",
                                        "test")
        top.add_positions_from_file(TEST_DATA + "/topology_test/test.pdb")
        for meta_mol in top.molecules:
            for node in meta_mol.molecule.nodes:
                assert "position" in meta_mol.molecule.nodes[node].keys()

        for meta_mol in top.molecules:
            for node in meta_mol.nodes:
                assert "position" in meta_mol.nodes[node].keys()
                assert meta_mol.nodes[node]["build"] == False

    @staticmethod
    def test_convert_to_vermouth_system():
        top = Topology.from_gmx_topfile(
            TEST_DATA + "/topology_test/system.top", "test")
        system = top.convert_to_vermouth_system()
        assert isinstance(system, vermouth.system.System)
        assert len(system.molecules) == 1

    @staticmethod
    @pytest.mark.parametrize('lines, outcome', (("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ atomtypes ]
        O       8 0.000 0.000  A   2.7106496e-03  9.9002500e-07
        C       8 0.000 0.000  A   1.7106496e-03  9.9002500e-07
        """, {
        frozenset(["O", "O"]): {
            "f": 1,
            "nb1": 2.7106496e-03,
            "nb2": 9.9002500e-07
        },
        frozenset(["C", "C"]): {
            "f": 1,
            "nb1": 1.7106496e-03,
            "nb2": 9.9002500e-07
        },
        frozenset(["C", "O"]): {
            "f": 1,
            "nb1": 0.0022106496,
            "nb2": 9.9002500e-07
        }
    }), ("""
        [ defaults ]
        1.0   3.0    yes  0.5     0.5
        [ atomtypes ]
        C   C   6      12.01100     0.500       A    3.75000e-01  4.39320e-01 ; SIG
        O   O   8      15.99940    -0.500       A    2.96000e-01  8.78640e-01 ; SIG
        """, {
        frozenset(["C", "C"]): {
            "f": 1,
            "nb1": 3.75000e-01,
            "nb2": 4.39320e-01
        },
        frozenset(["O", "O"]): {
            "f": 1,
            "nb1": 2.96000e-01,
            "nb2": 8.78640e-01
        },
        frozenset(["O", "C"]): {
            "f": 1,
            "nb1": 0.3355,
            "nb2": 0.6212923022217481
        }
    }), ("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ atomtypes ]
        O       8 0.000 0.000  A   2.7106496e-03  9.9002500e-07
        C       8 0.000 0.000  A   1.7106496e-03  9.9002500e-07
        [ nonbond_params ]
        C    O    1     2.0     4.0
        """, {
        frozenset(["O", "O"]): {
            "f": 1,
            "nb1": 2.7106496e-03,
            "nb2": 9.9002500e-07
        },
        frozenset(["C", "C"]): {
            "f": 1,
            "nb1": 1.7106496e-03,
            "nb2": 9.9002500e-07
        },
        frozenset(["C", "O"]): {
            "f": 1,
            "nb1": 2.0,
            "nb2": 4.0
        }
    })))
    def test_gen_pairs(lines, outcome):
        new_lines = textwrap.dedent(lines)
        new_lines = new_lines.splitlines()
        force_field = vermouth.forcefield.ForceField(name='test_ff')
        top = Topology(force_field, name="test")
        polyply.src.top_parser.read_topology(new_lines, top)
        top.gen_pairs()
        for atom_pair in outcome:
            assert atom_pair in top.nonbond_params
            nb1 = top.nonbond_params[atom_pair]["nb1"]
            nb2 = top.nonbond_params[atom_pair]["nb2"]
            nb1_ref = outcome[atom_pair]["nb1"]
            nb2_ref = outcome[atom_pair]["nb2"]
            assert math.isclose(nb1, nb1_ref)
            assert math.isclose(nb2, nb2_ref)

    @staticmethod
    @pytest.mark.parametrize(
        'lines, outcome',
        (
            ("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ atomtypes ]
        O       8 0.000 0.000  A   2.7106496e-03  9.9002500e-07
        C       8 0.000 0.000  A   1.7106496e-03  9.9002500e-07
        #define ga_1  100  250
        [ moleculetype ]
        test 3
        [ atoms ]
        1 CH3   1 test C1 1   0.0 14.0
        2 CH2   1 test C2 2   0.0 12.0
        3 CH3   1 test C3 3   0.0 12.0
        [ angles ]
        1 2  3 2 ga_1
        [ system ]
        some title
        [ molecules ]
        test 1
        """, ["2", "100", "250"]),
            # different location of define statement
            ("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        #define ga_1  100  250
        [ atomtypes ]
        O       8 0.000 0.000  A   2.7106496e-03  9.9002500e-07
        C       8 0.000 0.000  A   1.7106496e-03  9.9002500e-07
        [ moleculetype ]
        test 3
        [ atoms ]
        1 CH3   1 test C1 1   0.0 14.0
        2 CH2   1 test C2 2   0.0 12.0
        3 CH3   1 test C3 3   0.0 12.0
        [ angles ]
        1 2  3 2 ga_1
        [ system ]
        some title
        [ molecules ]
        test 1
        """, ["2", "100", "250"]),
            # two defines for one statement
            ("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        #define ga_1  100
        #define gk_1  250
        [ atomtypes ]
        O       8 0.000 0.000  A   2.7106496e-03  9.9002500e-07
        C       8 0.000 0.000  A   1.7106496e-03  9.9002500e-07
        [ moleculetype ]
        test 3
        [ atoms ]
        1 CH3   1 test C1 1   0.0 14.0
        2 CH2   1 test C2 2   0.0 12.0
        3 CH3   1 test C3 3   0.0 12.0
        [ angles ]
        1 2  3 2 ga_1 gk_1
        [ system ]
        some title
        [ molecules ]
        test 1
        """, ["2", "100", "250"])))
    def test_replace_defines(lines, outcome):
        new_lines = textwrap.dedent(lines)
        new_lines = new_lines.splitlines()
        force_field = vermouth.forcefield.ForceField(name='test_ff')
        top = Topology(force_field, name="test")
        polyply.src.top_parser.read_topology(new_lines, top)
        top.replace_defines()
        assert top.molecules[0].molecule.interactions["angles"][
            0].parameters == outcome

    @staticmethod
    def test_convert_nonbond_to_sig_eps():
        """
        Simply test if the conversion from C6 C12 to simga epsilon
        is done properly.
        """

        force_field = vermouth.forcefield.ForceField(name='test_ff')
        top = Topology(force_field, name="test")
        top.nonbond_params = {
            frozenset(["EO", "EO"]): {
                "nb1": 6.44779031E-02,
                "nb2": 4.07588234E-04
            }
        }
        top.convert_nonbond_to_sig_eps()
        assert math.isclose(top.nonbond_params[frozenset(["EO", "EO"])]["nb1"],
                            0.43)
        assert math.isclose(top.nonbond_params[frozenset(["EO", "EO"])]["nb2"],
                            3.4 * 0.75)

    @staticmethod
    @pytest.mark.parametrize(
        'lines, outcome',
        (
            ("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ bondtypes ]
        C       C       1       0.1335  502080.0
        [ moleculetype ]
        test 3
        [ atoms ]
        1 C   1 test C1 1   0.0 14.0
        2 C   1 test C2 2   0.0 12.0
        [ bonds ]
        1 2  1
        [ system ]
        some title
        [ molecules ]
        test 1
        """, {
                "bonds": [
                    Interaction(atoms=(0, 1),
                                parameters=["1", "0.1335", "502080.0"],
                                meta={})
                ]
            }),
            # test three element define
            ("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ angletypes ]
        CE1   CE1	CT2	5	123.50	401.664	0.0	0.0
        [ moleculetype ]
        test 3
        [ atoms ]
        1 CE1   1 test C1 1   0.0 14.0
        2 CE1   1 test C2 2   0.0 12.0
        3 CT2   1 test C3 3   0.0 12.0
        [ angles ]
        1 2  3 1
        [ system ]
        some title
        [ molecules ]
        test 1
        """, {
                "angles": [
                    Interaction(
                        atoms=(0, 1, 2),
                        parameters=["5", "123.50", "401.664", "0.0", "0.0"],
                        meta={})
                ]
            }),
            # test reverse match
            ("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ angletypes ]
        CE1    CE2	CT2	5	123.50	401.664	0.0	0.0
        [ moleculetype ]
        test 3
        [ atoms ]
        1 CT2   1 test C1 1   0.0 14.0
        2 CE2   1 test C2 2   0.0 12.0
        3 CE1   1 test C3 3   0.0 12.0
        [ angles ]
        1  2  3 1
        [ system ]
        some title
        [ molecules ]
        test 1
        """, {
                "angles": [
                    Interaction(
                        atoms=(0, 1, 2),
                        parameters=["5", "123.50", "401.664", "0.0", "0.0"],
                        meta={})
                ]
            }),
            # test generic match
            ("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ dihedraltypes ]
        X  CE2  CE1  X    5   123.50	401.664	0.0	0.0
        X  CT2  CE1  X    5   120	400	0.0	0.0
        X  QQQ  QQQ  X    5   150	400	0.0	0.0
        [ moleculetype ]
        test 3
        [ atoms ]
        1 CT2   1 test C1 1   0.0 14.0
        2 CE2   1 test C2 2   0.0 12.0
        3 CE1   1 test C3 3   0.0 12.0
        4 CT2   1 test C4 4   0.0 12.0
        5 CT2   1 test C5 5   0.0 14.0
        [ dihedrals ]
        1  2  3  4 1
        2  3  4  5 1
        [ system ]
        some title
        [ molecules ]
        test 1
        """, {
                "dihedrals": [
                    Interaction(
                        atoms=(0, 1, 2, 3),
                        parameters=["5", "123.50", "401.664", "0.0", "0.0"],
                        meta={}),
                    Interaction(atoms=(1, 2, 3, 4),
                                parameters=["5", "120", "400", "0.0", "0.0"],
                                meta={})
                ]
            }),
            # test generic match plus defined match on same pattern
            ("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ dihedraltypes ]
        X  CE2  CE1  X    5   123.50	401.664	0.0	0.0
        X  CT2  CE1  X    5   123.50	401.664	0.0	0.0
        [ moleculetype ]
        test 3
        [ atoms ]
        1 CT2   1 test C1 1   0.0 14.0
        2 CE2   1 test C2 2   0.0 12.0
        3 CE1   1 test C3 3   0.0 12.0
        4 CT2   1 test C4 4   0.0 12.0
        5 CT2   1 test C5 5   0.0 14.0
        [ dihedrals ]
        1  2  3  4 1
        2  3  4  5 1   150  60  0.0 0.0
        [ system ]
        some title
        [ molecules ]
        test 1
        """, {
                "dihedrals": [
                    Interaction(
                        atoms=(0, 1, 2, 3),
                        parameters=["5", "123.50", "401.664", "0.0", "0.0"],
                        meta={}),
                    Interaction(atoms=(1, 2, 3, 4),
                                parameters=["1", "150", "60", "0.0", "0.0"],
                                meta={})
                ]
            }),
            # test priority of defined over generic match
            ("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ dihedraltypes ]
        CT2  CE2  CE1  CT2    5   123.50	401.664	0.0	0.0
        X    CE2  CE1  X      5   20            20      0.0     0.0
        [ moleculetype ]
        test 3
        [ atoms ]
        1 CT2   1 test C1 1   0.0 14.0
        2 CE2   1 test C2 2   0.0 12.0
        3 CE1   1 test C3 3   0.0 12.0
        4 CT2   1 test C4 4   0.0 12.0
        5 CT2   1 test C5 5   0.0 14.0
        [ dihedrals ]
        1  2  3  4 1
        [ system ]
        some title
        [ molecules ]
        test 1
        """, {
                "dihedrals": [
                    Interaction(
                        atoms=(0, 1, 2, 3),
                        parameters=["5", "123.50", "401.664", "0.0", "0.0"],
                        meta={})
                ]
            }),
            # test reverse order for priority of defined over generic match
            ("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ dihedraltypes ]
        X    CE2  CE1  X      5   20            20      0.0     0.0
        CT2  CE2  CE1  CT2    5   123.50	401.664	0.0	0.0
        [ moleculetype ]
        test 3
        [ atoms ]
        1 CT2   1 test C1 1   0.0 14.0
        2 CE2   1 test C2 2   0.0 12.0
        3 CE1   1 test C3 3   0.0 12.0
        4 CT2   1 test C4 4   0.0 12.0
        5 CT2   1 test C5 5   0.0 14.0
        [ dihedrals ]
        1  2  3  4 1
        [ system ]
        some title
        [ molecules ]
        test 1
        """, {
                "dihedrals": [
                    Interaction(
                        atoms=(0, 1, 2, 3),
                        parameters=["5", "123.50", "401.664", "0.0", "0.0"],
                        meta={})
                ]
            }),
            # test generic improper
            ("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ dihedraltypes ]
        CT2   X     X     CT2    5   123.50	401.664	0.0	0.0
        CT2   X     X     CE2    5   123.50	401.664	0.0	0.0
        [ moleculetype ]
        test 3
        [ atoms ]
        1 CT2   1 test C1 1   0.0 14.0
        2 CE2   1 test C2 2   0.0 12.0
        3 CE1   1 test C3 3   0.0 12.0
        4 CT2   1 test C4 4   0.0 12.0
        5 CT2   1 test C5 5   0.0 14.0
        [ dihedrals ]
        1  2  3  4 2
        2  3  4  5 2
        [ system ]
        some title
        [ molecules ]
        test 1
        """, {
                "dihedrals": [
                    Interaction(
                        atoms=(0, 1, 2, 3),
                        parameters=["5", "123.50", "401.664", "0.0", "0.0"],
                        meta={}),
                    Interaction(
                        atoms=(1, 2, 3, 4),
                        parameters=["5", "123.50", "401.664", "0.0", "0.0"],
                        meta={})
                ]
            }),
            # multiple matchs and pairs and a meta parameters
            ("""
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ angletypes ]
        CE1    CE2	CT2	5	123.50	401.664	0.0	0.0
        [ bondtypes ]
        CT2       CE2       1       0.1335  502080.0
        #ifdef old
        CE2       CE1       1       0.1335  502080.0
        #endif
        [ moleculetype ]
        test 3
        [ atoms ]
        1 CT2   1 test C1 1   0.0 14.0
        2 CE2   1 test C2 2   0.0 12.0
        3 CE1   1 test C3 3   0.0 12.0
        [ bonds ]
        1 2 1
        2 3 1
        [ pairs ]
        1  2  1
        [ angles ]
        #ifdef angle
        1  2  3 1
        #endif
        [ system ]
        some title
        [ molecules ]
        test 1
        """, {
                "bonds": [
                    Interaction(atoms=(0, 1),
                                parameters=["1", "0.1335", "502080.0"],
                                meta={}),
                    Interaction(atoms=(1, 2),
                                parameters=["1", "0.1335", "502080.0"],
                                meta={
                                    'tag': 'old',
                                    'condition': 'ifdef'
                                })
                ],
                "pairs":
                [Interaction(atoms=(0, 1), parameters=["1"], meta={})],
                "angles": [
                    Interaction(
                        atoms=(0, 1, 2),
                        parameters=["5", "123.50", "401.664", "0.0", "0.0"],
                        meta={"ifdef": "angle"})
                ]
            }),
            # test bondtype usage
            ("""
        #define _FF_OPLS
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ atomtypes ]
        opls_001   C   6      12.01100     0.500       A    3.75000e-01  4.39320e-01 ; SIG
        [ bondtypes ]
        C       C       1       0.1335  502080.0
        [ moleculetype ]
        test 3
        [ atoms ]
        1 opls_001   1 test C1 1   0.0 14.0
        2 opls_001   1 test C2 2   0.0 12.0
        [ bonds ]
        1 2  1
        [ system ]
        some title
        [ molecules ]
        test 1
        """, {
                "bonds": [
                    Interaction(atoms=(0, 1),
                                parameters=["1", "0.1335", "502080.0"],
                                meta={})
                ]
            }),
            # test virtual_sites n,2,3,4 are skipped
            ("""
        #define _FF_OPLS
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ atomtypes ]
        opls_001   C   6      12.01100     0.500       A    3.75000e-01  4.39320e-01 ; SIG
        [ bondtypes ]
        C       C       1       0.1335  502080.0
        [ moleculetype ]
        test 3
        [ atoms ]
        1 opls_001   1 test C1 1   0.0 14.0
        2 opls_001   1 test C2 2   0.0 12.0
        3 opls_001   1 test C2 2   0.0 12.0
        4 opls_001   1 test C2 2   0.0 12.0
        5 opls_001   1 test C2 2   0.0 12.0
        6 opls_001   1 test C2 2   0.0 12.0
        7 opls_001   1 test C2 2   0.0 12.0
        8 opls_001   1 test C2 2   0.0 12.0
        9 opls_001   1 test C2 2   0.0 12.0
        10 opls_001   1 test C2 2   0.0 12.0
        [ bonds ]
        1  2  1
        1  8  1
        1  9  1
        2  9  1
        8  9  1
        ; currently not parsed accurately due to vermouth bug
        [ virtual_sites2 ]
        4   1  9  1  0.5000
        [ virtual_sites3 ]
        5   4  8  1  1  0.200  0.200
        6   4  9  2  1  0.200  0.200
        [ virtual_sites4 ]
        10   4  8  1  7  1  0.200  0.200  0.300
        [ virtual_sitesn ]
        3   1   4   4   1  2
        7   1   4   4   8  9
        [ system ]
        some title
        [ molecules ]
        test 1
        """, {
                "bonds": [
                    Interaction(atoms=(0, 1),
                                parameters=["1", "0.1335", "502080.0"],
                                meta={}),
                    Interaction(atoms=(0, 7),
                                parameters=["1", "0.1335", "502080.0"],
                                meta={}),
                    Interaction(atoms=(0, 8),
                                parameters=["1", "0.1335", "502080.0"],
                                meta={}),
                    Interaction(atoms=(1, 8),
                                parameters=["1", "0.1335", "502080.0"],
                                meta={}),
                    Interaction(atoms=(7, 8),
                                parameters=["1", "0.1335", "502080.0"],
                                meta={})
                ],
                "virtual_sitesn": [
                    Interaction(
                        atoms=(2, 3, 3, 0, 1), parameters=["1"], meta={}),
                    Interaction(
                        atoms=(6, 3, 3, 7, 8), parameters=["1"], meta={})
                ],
                "virtual_sites4": [
                    Interaction(atoms=(9, 3, 7, 0, 6),
                                parameters=["1", "0.200", "0.200", "0.300"],
                                meta={})
                ],
                "virtual_sites2": [
                    Interaction(
                        atoms=(3, 0, 8), parameters=["1", "0.5000"], meta={})
                ],
                "virtual_sites3": [
                    Interaction(atoms=(4, 3, 7, 0),
                                parameters=["1", "0.200", "0.200"],
                                meta={}),
                    Interaction(atoms=(5, 3, 8, 1),
                                parameters=["1", "0.200", "0.200"],
                                meta={})
                ]
            })))
    def test_replace_types(lines, outcome):
        new_lines = textwrap.dedent(lines)
        new_lines = new_lines.splitlines()
        force_field = vermouth.forcefield.ForceField(name='test_ff')
        top = Topology(force_field, name="test")
        polyply.src.top_parser.read_topology(new_lines, top)
        top.gen_bonded_interactions()
        for inter_type in outcome:
            assert top.molecules[0].molecule.interactions[
                inter_type] == outcome[inter_type]

    @staticmethod
    def test_replace_types_fail():
        lines = """
        [ defaults ]
        1.0   1.0   yes  1.0     1.0
        [ dihedraltypes ]
        C   Q    Q     Q    5   123.50	401.664	0.0	0.0
        [ moleculetype ]
        test 3
        [ atoms ]
        1 CT2   1 test C1 1   0.0 14.0
        2 CE2   1 test C2 2   0.0 12.0
        3 CE1   1 test C3 3   0.0 12.0
        4 CT2   1 test C4 4   0.0 12.0
        [ dihedrals ]
        1  2  3  4 2
        [ system ]
        some title
        [ molecules ]
        test 1
        """
        new_lines = textwrap.dedent(lines)
        new_lines = new_lines.splitlines()
        force_field = vermouth.forcefield.ForceField(name='test_ff')
        top = Topology(force_field, name="test")
        polyply.src.top_parser.read_topology(new_lines, top)
        with pytest.raises(OSError):
            top.gen_bonded_interactions()
 ; name nexcl.
 PEO         1
 ;
 [ atoms ]
 1  SN1a    1   PEO   CO1  1   0.000  45
 2  SN1a    1   PEO   CO2  1   0.000  45
 3  SN1a    1   PEO   CO3  1   0.000  45
 4  SN1a    1   PEO   CO4  1   0.000  45
 [ bonds ]
 ; back bone bonds
 1  2   1   0.37 7000
 2  3   1   0.37 8000
 3  4   1   0.37 9000
 """,
 ["PEO", "PEO"],
 [Interaction(atoms=(0, 1), parameters=['1', '0.37', '7000'], meta={}),
  Interaction(atoms=(1, 2), parameters=['1', '0.37', '8000'], meta={}),
  Interaction(atoms=(2, 3), parameters=['1', '0.37', '9000'], meta={}),
  Interaction(atoms=(4, 5), parameters=['1', '0.37', '7000'], meta={}),
  Interaction(atoms=(5, 6), parameters=['1', '0.37', '8000'], meta={}),
  Interaction(atoms=(6, 7), parameters=['1', '0.37', '9000'], meta={})],
 [(0, 1), (1, 2), (2, 3), (4, 5), (5, 6), (6, 7)],
 8,
 {}),
 # test multiblock from-itp
 ("""
 [ moleculetype ]
 ; name nexcl.
 PEO         1
 ;
 [ atoms ]
def test_pre_post_section_lines(pre_meta, post_meta):
    """
    :func:`vermouth.gmx.itp.write_molecule_itp` writes pre and post section lines.

    If `pre_meta` or `post_meta` is `True`, then "pre_section_lines" or
    "post_section_lines", respectively, is read from `molecule.meta`, otherwise
    it is passed as an argument.
    """
    molecule = Molecule(nrexcl=1)
    molecule.add_nodes_from((
        (0, {
            'atype': 'Q5',
            'resid': 1,
            'resname': 'VAL',
            'atomname': 'BB',
            'charge_group': 1,
            'charge': 1,
        }),
        (1, {
            'atype': 'Q5',
            'resid': 1,
            'resname': 'VAL',
            'atomname': 'BB',
            'charge_group': 1,
            'charge': 1,
        }),
        (2, {
            'atype': 'Q5',
            'resid': 1,
            'resname': 'VAL',
            'atomname': 'BB',
            'charge_group': 1,
            'charge': 1,
        }),
    ))
    # The molecule has 6 interaction sections. The sections are names
    # "interaction_<joint>_<part>", where <joint> can be "with" or "only", and
    # <part> can be "pre", "post", or "pre_post". The <part> indicates what
    # pre_section_lines and post_section_lines are defined for the section.
    # When <joint> in "with", then the section contains 2 interactions in
    # addition to the pre- and post- lines. When <joint> is "only", then the
    # section only has the pre/post lines defined, and no interactions.
    parts = ('pre', 'post', 'pre_post')
    molecule.interactions = {}
    for part in parts:
        name = 'interaction_with_{}'.format(part)
        molecule.interactions[name] = [
            Interaction(atoms=[0, 1], parameters=[name], meta={}),
            Interaction(atoms=[1, 2], parameters=[name], meta={}),
        ]

    pre_section_lines = {
        'atoms': ['test_atoms_0', 'test_atoms_1'],
        'interaction_with_pre': ['test_pre_0', 'test_pre_1', 'test_pre_2'],
        'interaction_with_pre_post': ['test_pre_post_0'],
        'interaction_only_pre': ['test_pre_only_0', 'test_pre_only_1'],
        'interaction_only_pre_post':
        ['test_prepost_only_0', 'test_prepost_only_1'],
    }
    post_section_lines = {
        'atoms': ['after_atoms_0'],
        'interaction_with_post': ['after_post_0', 'after_post_1'],
        'interaction_with_pre_post': ['after_pre_post_0'],
        'interaction_only_post': ['after_post_only_0', 'after_post_only_1'],
        'interaction_only_pre_post': ['after_prepost_only_0'],
    }
    expected_segments = [
        """
        [ atoms ]
        test_atoms_0
        test_atoms_1
        1 Q5 1 VAL BB 1 1 
        2 Q5 1 VAL BB 1 1 
        3 Q5 1 VAL BB 1 1 
        after_atoms_0
        """,
        """
        [ interaction_with_pre ]
        test_pre_0
        test_pre_1
        test_pre_2
        1 2 interaction_with_pre
        2 3 interaction_with_pre
        """,
        """
        [ interaction_with_post ]
        1 2 interaction_with_post
        2 3 interaction_with_post
        """,
        """
        [ interaction_with_pre_post ]
        test_pre_post_0
        1 2 interaction_with_pre_post
        2 3 interaction_with_pre_post
        after_pre_post_0
        """,
        """
        [ interaction_only_pre ]
        test_pre_only_0
        test_pre_only_1
        """,
        """
        [ interaction_only_post ]
        after_post_only_0
        after_post_only_1
        """,
        """
        [ interaction_only_pre_post ]
        test_prepost_only_0
        test_prepost_only_1
        after_prepost_only_0
        """,
    ]

    if post_meta:
        arg_post = post_section_lines
        molecule.meta['post_section_lines'] = 'invalid'
    else:
        arg_post = None
        molecule.meta['post_section_lines'] = post_section_lines
    if pre_meta:
        arg_pre = pre_section_lines
        molecule.meta['pre_section_lines'] = 'invalid'
    else:
        arg_pre = None
        molecule.meta['pre_section_lines'] = pre_section_lines

    outfile = io.StringIO()
    write_molecule_itp(molecule,
                       outfile,
                       moltype='test',
                       post_section_lines=arg_post,
                       pre_section_lines=arg_pre)
    itp_content = outfile.getvalue()
    # This could be a assert all(...), but it makes it more difficult to
    # understant what is happening in case of a failure.
    for segment in expected_segments:
        assert textwrap.dedent(segment)[:-1] in itp_content