Source code for cmmdft.external_potential.parameters

# -*- coding: utf-8 -*-
# YAFF is yet another force-field code.
# Copyright (C) 2011 Toon Verstraelen <Toon.Verstraelen@UGent.be>,
# Louis Vanduyfhuys <Louis.Vanduyfhuys@UGent.be>, Center for Molecular Modeling
# (CMM), Ghent University, Ghent, Belgium; all rights reserved unless otherwise
# stated.
#
# This file is part of YAFF.
#
# YAFF is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# YAFF is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>
#
# --
'''Object-oriented representation of parameter files'''


from __future__ import division
from __future__ import print_function


__all__ = ['Complain', 'Parameters', 'ParameterSection', 'ParameterDefinition']


[docs] class Complain(object): '''Class for complain method of ParameterFile and ParameterSection''' def __init__(self, filename='__nofile__'): self.filename = filename def __call__(self, counter, message): if counter is None: raise IOError('The parameter file %s %s.' % (self.filename, message)) else: raise IOError('Line %i in the parameter file %s %s.' % (counter, self.filename, message))
[docs] class Parameters(object): '''Object that represents a force field parameter file The parameter file is first parsed by this object into a convenient data structure with dictionaries. The actual force field is then generated based on these dictionaries. The parameter file has a purely line-based syntax. The order of the lines has no meaning. Comments begin with a hash sign (#) and continue till the end of a line. If the line is empty after stripping the comments, it is ignored. Every non-empty line should have the following format: PREFIX:SUFFIX DATA The prefix is used for sections, the suffix for definitions and the remainder of the line contains arguments for the definition. Definitions may be repeated with different or the same arguments. ''' def __init__(self, sections=None): if sections is None: self.sections = {} else: self.sections = sections
[docs] @classmethod def from_file(cls, filenames): '''Create a Parameters instance from one or more text files. **Arguments:** filenames A single filename or a list of filenames ''' def parse_line(line, complain): '''parse a single line''' pos = line.find(':') if pos == -1: complain(counter, 'does not contain a colon') prefix = line[:pos].upper() rest = line[pos+1:].strip() if len(rest) == 0: complain(counter, 'does not have text after the colon') if len(prefix.split()) > 1: complain(counter, 'has a prefix that contains whitespace') pos = rest.find(' ') if pos == 0: complain(counter, 'does not have a definition after the prefix') elif pos == -1: complain(counter, 'does not have data after the definition') suffix = rest[:pos].upper() data = rest[pos+1:] return prefix, suffix, data if isinstance(filenames, str): filenames = [filenames] result = cls({}) for filename in filenames: with open(filename) as f: counter = 1 complain = Complain(filename) for line in f: line = line[:line.find('#')].strip() if len(line) > 0: # parse single line prefix, suffix, data = parse_line(line, complain) # get/make section section = result.sections.get(prefix) if section is None: section = ParameterSection(prefix, {}, complain) result.sections[prefix] = section # get/make definition definition = section.definitions.get(suffix) if definition is None: definition = ParameterDefinition(suffix, [], complain) section.definitions[suffix] = definition definition.lines.append((counter, data)) counter += 1 return result
[docs] def copy(self): '''Return an independent copy''' sections = {} for prefix, section in self.sections.items(): sections[prefix] = section.copy() return Parameters(sections)
[docs] def write_to_file(self, filename): '''Write the parameters back to a file The outut file will not contain any comments. ''' with open(filename, 'w') as f: for prefix, section in self.sections.items(): for suffix, definition in section.definitions.items(): for counter, data in definition.lines: print('%s:%s %s' % (prefix, suffix, data), file=f) print(file=f) print(file=f) print(file=f)
def __getitem__(self, prefix): result = self.sections.get(prefix.upper()) if result is None: result = ParameterSection(prefix, {}) return result
[docs] class ParameterSection(object): '''Object that represents one section in a force field parameter file''' def __init__(self, prefix, definitions=None, complain=None): self.prefix = prefix if definitions is None: self.definitions = {} else: self.definitions = definitions if complain is None: self.complain = Complain() else: self.complain = complain def __getitem__(self, suffix): result = self.definitions.get(suffix.upper()) if result is None: result = ParameterDefinition(suffix, [], self.complain) return result
[docs] def copy(self): '''Return an independent copy''' definitions = {} for suffix, definition in self.definitions.items(): definitions[suffix] = definition.copy() return ParameterSection(self.prefix, definitions, self.complain)
[docs] class ParameterDefinition(object): '''Object that represents a set of data lines from a parameter file''' def __init__(self, suffix, lines=None, complain=None): self.suffix = suffix if lines is None: self.lines = [] else: self.lines = lines if complain is None: self.complain = Complain() else: self.complain = complain def __getitem__(self, index): return self.lines[index] def __iter__(self): return iter(self.lines)
[docs] def copy(self): lines = [] for counter, data in self.lines: lines.append((counter, data)) return ParameterDefinition(self.suffix, lines, self.complain)