""":module AbstractBaseCalculator: Hosting the base class of all Calculators."""
##########################################################################
# #
# Copyright (C) 2015-2018 Carsten Fortmann-Grote #
# 2016-2017 Sergey Yakubov #
# Contact: Carsten Fortmann-Grote <carsten.grote@xfel.eu> #
# #
# This file is part of simex_platform. #
# simex_platform 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. #
# #
# simex_platform 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/>. #
# #
##########################################################################
from abc import ABCMeta, abstractmethod
from SimEx.AbstractBaseClass import AbstractBaseClass
from SimEx.Parameters.AbstractCalculatorParameters import AbstractCalculatorParameters
from SimEx.Utilities import ParallelUtilities
from SimEx.Utilities.EntityChecks import checkAndSetInstance
import dill
import os
import sys
[docs]class AbstractBaseCalculator(AbstractBaseClass, metaclass=ABCMeta):
"""
:class AbstractBaseCalculator: Abstract class for all simulation calculators.
"""
@abstractmethod
def __init__(self, parameters=None, input_path=None, output_path=None):
"""
:param parameters: Parameters of the calculation (not data).
:type parameters: dict || AbstractCalculatorParameters
:param input_path: Path to hdf5 file holding the input data.
:type input_path: str
:param output_path: Path to hdf5 file for output.
:type output_path: str
"""
# Check parameters.
self.__parameters = checkAndSetParameters(parameters)
self.__input_path, self.__output_path = checkAndSetIO((input_path, output_path))
[docs] @classmethod
def runFromCLI(cls):
"""
Method to start calculator computations from command line.
:return: exit with status code
"""
if len(sys.argv) == 2:
fname = sys.argv[1]
calculator=cls.dumpLoader(fname)
status = calculator._run()
sys.exit(status)
[docs] @classmethod
def dumpLoader(cls,fname):
"""
Creates calculator object from a dump file
:param fname: path to the dump file.
:return: Created calculator object.
:raises RuntimeError: if cannot create object.
"""
try:
with open(fname,'rb') as file_handle:
calculator = dill.load(file_handle)
except:
raise IOError("Cannot read from file "+fname)
if not issubclass(type(calculator),AbstractBaseCalculator):
raise TypeError( "The argument to the script should be a path to a file "
"with object of subclass of AbstractBaseCalculator")
return calculator
[docs] @abstractmethod
def backengine(self):
"""
Method to call the backengine for the calculator.
To be implemented on the derived classes.
"""
pass
[docs] @abstractmethod
def expectedData(self):
"""
Query for the data fields expected by this calculator.
"""
# To be implemented in specialized calculators.
pass
[docs] @abstractmethod
def providedData(self):
"""
Query for the data fields provided by this calculator.
"""
# To be implemented by specialized calculator.
pass
# @abstractmethod
def _run(self):
"""
Method to do computations. By default starts backengine.
:return: status code.
"""
result=self.backengine()
if result is None:
result=0
return result
# Can be reimplemented by specialized calculator.
[docs] def dumpToFile(self, fname):
"""
dump class instance to file.
:param fname: Path to file to dump.
"""
try:
with open(fname, "wb") as file_handle:
dill.dump(self, file_handle)
except:
raise IOError("Cannot dump to file "+fname)
@abstractmethod
def _readH5(self):
pass
[docs] @abstractmethod
def saveH5(self):
pass
#######################################################################
# Queries and setters
#######################################################################
# control_parameters
@property
def parameters(self):
""" Query for the control parameters of the calculator."""
return self.__parameters
@parameters.setter
def parameters(self, value):
""" Set the control parameters for the calculation. """
if isinstance( value, AbstractCalculatorParameters):
self.__parameters = value
return
self.__parameters = checkAndSetInstance(dict, value, None)
@parameters.deleter
def parameters(self):
""" Delete the control parameters. """
del self.__parameters
# input
@property
def input_path(self):
""" Query for the input file path(s). """
return self.__input_path
@input_path.setter
def input_path(self, value):
""" Set the io path(s) to a value. """
self.__input_path = checkAndSetInstance( (str, list), value, None )
@input_path.deleter
def input_path(self):
""" Delete the input_path path(s). """
del self.__input_path
# output
@property
def output_path(self):
""" Query for the output file path(s). """
return self.__output_path
@output_path.setter
def output_path(self, value):
""" Set the io path(s) to a value. """
self.__output_path = checkAndSetInstance( (str, list), value, None )
@output_path.deleter
def output_path(self):
""" Delete the output_path path(s). """
del self.__output_path
[docs]def checkAndSetIO(io):
""" Check the passed io path/filenames and set appropriately. """
# Check we have a tuple.
io = checkAndSetInstance(tuple, io)
if len(io) != 2:
raise RuntimeError("The parameter 'io' can only be a tuple of two strings.")
# Check if input exists, if not, raise.
i = checkAndSetInstance(str, io[0])
if i is None:
raise IOError("The parameter 'input_path' must be a valid filename.")
i = os.path.abspath(i)
#
# Check if output file exists, otherwise attempt to create it.
o = checkAndSetInstance(str, io[1])
if o is None:
raise IOError("The parameter 'output_path' must be a valid filename.")
o = os.path.abspath(o)
return (i, o)
[docs]def checkAndSetBaseCalculator(var=None, default=None):
"""
Check if passed object is an AbstractBaseCalculator instance. If non is given, set to given default.
:param var: The object to check.
:param default: The default to use.
:return: Te checked photon source object.
:raises RuntimeError: if no valid BaseCalculator was given.
"""
return checkAndSetInstance(AbstractBaseCalculator, var, default)
[docs]def checkAndSetParameters(parameters):
""" Utility to check if the 'parameters' argument is valid input.
:param parameters: The parameters object to check.
:type parameters: dict or AbstractCalculatorParameters
:return: The checked parameters object.
"""
if parameters is None:
parameters = {}
if not (isinstance( parameters, dict ) or isinstance( parameters, AbstractCalculatorParameters) ):
raise TypeError( "The 'parameters' argument to the constructor must be of type dict or AbstractCalculatorParameters.")
# Return.
return parameters