Source code for pestifer.logparsers.psfgenlogparser

# Author Cameron F. Abrams, <cfa22@drexel.edu>

"""
psfgen log parsing utility
"""

import logging
import os
import re

from .logparser import LogParser, get_single

logger = logging.getLogger(__name__)

[docs] class PsfgenLogParser(LogParser): """ A class for parsing Psfgen log files. This class is a subclass of :class:`LogParser <pestifer.logparsers.logparser.LogParser>` and provides methods for reading, updating, and dumping Psfgen log data. Parameters ---------- basename : str The base name for the log parser. This is used to name the output log file. """ info_keys = ['Info) ', 'Info: '] """ A list of prefixes used to identify information lines in the Psfgen log file. """ psfgen_key = 'psfgen) ' """ The prefix used to identify Psfgen-specific lines in the log file. """ def __init__(self, basename='psfgen-logparser'): super().__init__() self.line_idx = [0] # byte offsets of lines self.processed_line_idx = [] self.metadata = {} self.basename = basename
[docs] def update(self, bytes: str): """ Update the Psfgen log parser with new bytes of data. This method appends the new bytes to the byte collector and processes the lines in the log file. Parameters ---------- bytes : bytes The bytes to update the log parser with. This can be a string or bytes object containing the log data. """ super().update(bytes) last_line_idx = self.line_idx[-1] addl_line_idx = [m.start()+1+last_line_idx for m in re.finditer(os.linesep,self.byte_collector[last_line_idx:])] scan_ldx = [last_line_idx] # in case last line was incomplete in a previous pass if len(addl_line_idx) > 1: scan_ldx.extend(addl_line_idx) for i, j in zip(scan_ldx[:-1], scan_ldx[1:]): if i not in self.processed_line_idx: line = self.byte_collector[i:j] self.process_line(line) self.processed_line_idx.append(i) if len(addl_line_idx) > 1: self.line_idx.extend(addl_line_idx) last_line = self.byte_collector[addl_line_idx[-1]:] if last_line.endswith(os.linesep): self.process_line(last_line) self.processed_line_idx.append(addl_line_idx[-1])
[docs] def process_line(self, line: str): """ Process a line from the Psfgen log file. This method identifies the type of line (information, Psfgen command) and processes it accordingly. Parameters ---------- line : str A line from the Psfgen log file to be processed. This can be a string or bytes object containing the log data. """ assert line.endswith(os.linesep), f'process_line: {line} does not end with os.linesep' if line.startswith(self.info_keys[0]): o = len(self.info_keys[0]) self.process_info_line(line[o:]) elif line.startswith(self.info_keys[1]): o = len(self.info_keys[1]) self.process_info_line(line[o:]) elif line.startswith(self.psfgen_key): o = len(self.psfgen_key) self.process_psfgen_line(line[o:])
[docs] def process_info_line(self, line: str): """ Process an information line from the Psfgen log file. This method extracts metadata from the line and updates the `metadata` attribute accordingly. Parameters ---------- line : str A line from the Psfgen log file that contains information to be processed. This can be a string or bytes object containing the log data. """ if 'Exiting normally' in line: self.metadata['success'] = True elif line.startswith('VMD'): tokens = [x.strip() for x in line.split()] if len(tokens) > 1: if 'platform' not in self.metadata: self.metadata['platform'] = tokens[2] if 'version' not in self.metadata: self.metadata['version'] = tokens[4]
[docs] def success(self): """ Check if the Psfgen execution was successful. This method checks if the metadata contains the `success` key, which indicates that the execution was successful. Returns ------- bool True if the log parsing was successful, False otherwise. """ if 'success' in self.metadata: return self.metadata['success'] return False
[docs] def process_psfgen_line(self, line: str): """ Process a Psfgen-specific line from the log file. This method extracts metadata from the line and updates the `metadata` attribute accordingly. Parameters ---------- line : str A line from the Psfgen log file that contains Psfgen-specific information to be processed. This can be a string or bytes object containing the log data. """ if line.startswith('total of'): if line.endswith('atoms'): self.metadata['number_of_atoms'] = int(get_single('total of', line)) elif line.endswith('bonds'): self.metadata['number_of_bonds'] = int(get_single('total of', line)) elif line.endswith('angles'): self.metadata['number_of_angles'] = int(get_single('total of', line)) elif line.endswith('dihedrals'): self.metadata['number_of_dihedrals'] = int(get_single('total of', line)) elif line.endswith('impropers'): self.metadata['number_of_impropers'] = int(get_single('total of', line)) elif line.endswith('cross-terms'): self.metadata['number_of_cross_terms'] = int(get_single('total of', line)) elif line.endswith('explicit exclusions'): self.metadata['number_of_exclusions'] = int(get_single('total of', line))