Source code for chemtools.calculators.calculator

# -*- coding: utf-8 -*-

#The MIT License (MIT)
#
#Copyright (c) 2014 Lukasz Mentel
#
#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"), to deal
#in the Software without restriction, including without limitation the rights
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#copies of the Software, and to permit persons to whom the Software is
#furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#SOFTWARE.

'''
An abstract class that should be subclassed when addding a new code interface.

All the methods should be implemented for the basisopt module to work with the
new code.
'''

import os
import re

from string import Template

from abc import ABCMeta, abstractmethod


[docs]class Calculator(): ''' Abstract class that should be subclassed when adding a new code interace. ''' __metaclass__ = ABCMeta def __init__(self, exevar=None, executable=None, runopts=None, scratch=None): self.exevar = exevar if executable is None and exevar is None: self.exe_found = False elif executable is None and os.getenv(self.exevar) is not None: self.executable = os.getenv(self.exevar) self.exe_found = True elif executable is not None and exevar is None: self.executable = executable self.exe_found = True elif executable is not None and exevar is not None: exefromvar = os.getenv(self.exevar) if os.path.abspath(exefromvar) == os.path.abspath(executable): self.executable = exefromvar self.exe_found = True else: raise ValueError('conflicting <executable> and <exevar> options specified: '\ '{0:s} != {1:s}'.format(executable, exefromvar)) self.runopts = runopts self.scratch = scratch @property def executable(self): 'Set the executable' return self._executable @executable.setter def executable(self, value): if os.path.exists(value): if os.path.isdir(value): raise OSError('{0} is a directory, not executable'.format(value)) elif os.path.isfile(value): if os.access(value, os.X_OK): self._executable = value else: raise OSError('file: {0} is not executable'.format(value)) else: raise OSError('unknown type of the executable: {0}'.format(value)) else: raise ValueError("executable: '{0}' does not exists".format(value)) @property def scratch(self): 'Return scratch' return self._scratch @scratch.setter def scratch(self, path): if path is None: self._scratch = os.path.join(os.getenv('HOME'), 'scratch') elif os.path.exists(path): self._scratch = path else: raise ValueError("Scratch directory doesn't exist: {}".format(path))
[docs] @abstractmethod def parse(self, fname, objective, regexp=None): ''' Parse the value of the ``objective`` from the file ``fname`` ''' pass
[docs] @abstractmethod def run(self, *args): ''' run a single job ''' pass
[docs] @abstractmethod def run_multiple(self, *args): ''' Run multiple jobs ''' pass
[docs] def accomplished(self, fname): ''' Return True if the job completed without errors ''' return self.parse(fname, 'accomplished') is True
[docs] @abstractmethod def write_input(self): ''' write the input file in the format of the code used ''' pass
def parse_objective(fname, regexp, reflags=re.I): ''' Wrapper for the parse methods Args: fname : :py:class:`str` Name of the file to be parsed regexp : :py:class:`str` Regular expression as raw string reflags : Flags for the regular expression compilation ''' if not os.path.exists(fname): raise ValueError("Output file: {0:s} doesn't exist in {1:s}".format(fname, os.getcwd())) rec = re.compile(regexp, flags=reflags) with open(fname, 'r') as fobj: for line in fobj: match = rec.search(line) if match: # check if a group was captured or not if match.lastindex is not None and match.lastindex > 0: return float(match.group(1)) else: return True else: return None class InputTemplate(Template): 'Modified string.Template class to be used for input rendering' delimiter = '%' idpattern = r'[a-z][_a-z0-9]*' def get_keys(self): ''' Parse the string and return a dict with possible keys to substitute. For most use case only the `named` fields are interesting. ''' keys = {} match = self.pattern.findall(self.template) for key, val in self.pattern.groupindex.items(): keys[key] = [x[val - 1] for x in match if x[val - 1] != ''] return keys