Source code for fast_forward.itp_parser_sub

# Copyright 2020 University of Groningen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from vermouth.gmx.itp_read import ITPDirector
from vermouth.parser_utils import split_comments
from vermouth.molecule import Interaction
import numpy as np

[docs] class FastForwardITPParser(ITPDirector): ''' Same as the ITP parser but allows stashing of comments with interactions. ''' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.current_comment = None
[docs] def parse(self, file_handle): """ Reads lines from `file_handle`, and calls :meth:`~vermouth.gmx.itp_read.ITPDirector.dispatch` to find which method to call to do the actual parsing. Yields the result of that call, if it's not `None`. At the end, calls :meth:`~vermouth.gmx.itp_read.ITPDirector.finalize`., and yields its results, iff it's not None. Parameters ---------- file_handle: collections.abc.Iterable[str] The data to parse. Should produce lines of data. Yields ------ object The results of dispatching to parsing methods, and of :meth:`~vermouth.gmx.itp_read.ITPDirector.finalize`. """ lineno = 0 for lineno, line in enumerate(file_handle, 1): line, comment = split_comments(line, self.COMMENT_CHAR) if self.COMMENT_CHAR in comment: atom_comment, _ = split_comments(comment, self.COMMENT_CHAR) self.current_comment = atom_comment else: self.current_comment = comment if not line: continue result = self.dispatch(line)(line, lineno) if result is not None: yield result result = self.finalize(lineno) if result is not None: yield result
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 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 = {} if self.current_comment: meta['comment'] = self.current_comment interaction = Interaction( atoms=treated_atoms, parameters=parameters, meta=meta,) context.interactions[section] = context.interactions.get(section, []) + [interaction]
[docs] def read_itp(lines, force_field): director = FastForwardITPParser(force_field) return list(director.parse(iter(lines)))
def _matching_angles(angles): ''' for a list of tuples that may contain integers in reversed order, return only one entry. e.g. for [(0,1,2),(1,2,3),(2,1,0)] we get [(0,1,2),(1,2,3)] ''' seen = set() unique_list = [] for tup in angles: sorted_tup = tuple(sorted(tup)) # Sort to ensure order-independent uniqueness if sorted_tup not in seen: seen.add(sorted_tup) unique_list.append(tup) # Append the original order return unique_list
[docs] def guess_interactions(block): """ From the bonds described in a block's interactions, generate all possible angles and dihedrals. block: :class:`vermouth.molecule.Block` """ # clear any existing angles and dihedrals to prevent duplicates if block.interactions.get('angles'): del block.interactions['angles'] if block.interactions.get('dihedrals'): del block.interactions['dihedrals'] block.make_edges_from_interactions() angles = _matching_angles(block.guess_angles()) dihedrals = _matching_angles(block.guess_dihedrals()) # add dummy interactions to block if they're not already there. for i in angles: if i not in [[int(j) for j in k.atoms] for k in block.interactions['angles']]: comment = '_'.join([f"{block.nodes[atom]['resid']}_{block.nodes[atom]['atomname']}" for atom in i]) block.add_interaction('angles', atoms=i, parameters=['2', '10', '10'], meta={'version': 0, 'comment': comment}) for i in dihedrals: if i not in [[int(j) for j in k.atoms] for k in block.interactions['dihedrals']]: comment = '_'.join([f"{block.nodes[atom]['resid']}_{block.nodes[atom]['atomname']}" for atom in i]) block.add_interaction('dihedrals', atoms=i, parameters=['1', '10', '1', '1'], meta={'version': 0, 'comment': comment})