def __init__(self, value, gridData, gridName='grid'):
        # if grid data is a dictionary, first convert to datacontainer
        if isinstance(gridData, dict):
            from src.DataContainer import DataContainer
            dc = DataContainer()
            dc.addData(gridName, gridData)
            gridData = dc
            del dc

        # check if a grid is provided in DC data
        dimensions = gridData.v(gridName, 'dimensions')
        dimNames = [dimensions[i] for i in range(0, len(value.shape))]
        NumericalFunctionBase.__init__(self, dimNames)

        # add grid (with name 'grid') and value
        self.addGrid(gridData)
        self.addValue(value)
        return
Example #2
0
class NumericalFunctionBase(FunctionBase):
    #Variables

    #Methods
    def __init__(self, dimNames):
        FunctionBase.__init__(self, dimNames)
        self.dataContainer = DataContainer()
        self.valueSize = 0
        return

    def function(self, **kwargs):
        if len([
                i for i in kwargs.keys()
                if i in self.dataContainer.v('grid', 'dimensions')
        ]) == 0:
            return self.__setReturnReference(kwargs.get('operation'))

        # evaluate function
        try:
            returnval = self.__evaluateFunction(**kwargs)
        except FunctionEvaluationError:
            returnval = self.__setReturnReference(kwargs.get('operation'))
        return returnval

    def __evaluateFunction(self, **kwargs):
        """Overrides the function method of FunctionBase, but is very similar.
        The difference is only that FunctionBase transfers kwargs to args before calling the actual functions
        Here we keep the kwargs as the actual functions also use this.
        """
        requestSize = sum(
            [dim in kwargs for dim in self.dimNames]
        )  # count the number of dimensions in kwargs (this makes sure that other parameters or irrelevant dimensions are ignored)

        operation = kwargs.get('operation')
        try:
            kwargs.pop('operation')
        except:
            pass
        # direct to actual function
        if operation is None:
            returnval = self.value(**kwargs)
        elif operation == 'd':
            returnval = self.derivative(**kwargs)
        elif operation == 'n':
            returnval = -self.value(**kwargs)
        elif operation == 'dn':
            returnval = -self.derivative(**kwargs)
        else:
            raise FunctionEvaluationError
        return returnval

    def __setReturnReference(self, operation=None):
        '''No difference with FunctionBase, but required here to refer to its own functions
        '''
        if not operation:
            returnval = self.function
        elif operation == 'n':
            returnval = self.negfunction
        elif operation == 'd':
            returnval = self.derfunction
        elif operation == 'dn':
            returnval = self.dnfunction
        else:
            raise KnownError(
                'Function called with unknown operation. (This error indicates an incorrectly defined function)'
            )
        return returnval

    def negfunction(self, **kwargs):
        """No difference with FunctionBase, but required here to refer to its own functions
        """
        # reset operations
        if kwargs.get(
                'operation'
        ) == 'n':  # if the negative of a negfunction is called, return to function.
            kwargs.pop('operation')
        elif kwargs.get('operation') == 'd':
            kwargs['operation'] = 'dn'
        else:
            kwargs['operation'] = 'n'

        # evaluate
        returnval = self.function(**kwargs)
        return returnval

    def addGrid(self, gridData, gridName='grid'):
        # set own DataContainer containing grid data
        data = gridData.slice(gridName, excludeKey=True)
        self.dataContainer.addData(
            'grid', data.data
        )  # improper use of the DataContainer by accessing its data directly
        return

    def addValue(self, value):
        """Add a variable 'value' to the numerical function

        Parameters:
            value (ndarray) - value to be put in the internal DataContainer
        """
        self.dataContainer.addData('value', value)
        self.valueSize = len(value.shape)
        return

    def addDerivative(self, derivative, dim):
        self.dataContainer.merge({'derivative': {dim: derivative}})
        return

    ### Depreciated v2.2 [dep01] ###
    def addSecondDerivative(self, derivative, dim):
        """ Depreciated v2.2
        """
        self.dataContainer.merge({'secondDerivative': {dim: derivative}})
        return

    ### End ###

    def value(self, **kwargs):
        """Return the value of the variable in this numerical function.

        Parameters:
            kwargs (dict) - coordinates

        Returns:
            Array value using DataContainer interpolation.
        """
        return self.dataContainer.v('value', **kwargs)

    def derivative(self, **kwargs):
        """Similar to .value(). Returns the derivative uploaded to the numerical function or makes a call
        to a numerical derivation method if no derivative is uploaded.
        """
        # kwargs.pop('operation')       # obsolete
        dim = kwargs.get('dim')
        v = self.dataContainer.v(
            'derivative', dim,
            **kwargs)  # try if analytical derivative is available
        if v is None:
            v = self.dataContainer.d(
                'value', **kwargs)  # else take numerical derivative
        return v

    ### Depreciated v2.2 [dep01] ###
    def secondDerivative(self, **kwargs):
        """See .derivative(). This method does the same for the second derivative
        Depreciated 2.2 [dep01]
        """
        # kwargs.pop('operation')       # obsolete
        dim = kwargs.get('dim')
        v = self.dataContainer.v('secondDerivative', dim, **kwargs)
        if v is None:
            v = self.dataContainer.dd('value', **kwargs)
        return v
Example #3
0
    def run(self):
        """invoke the saveData() method to save by using Pickle.

        Returns:
            Empty dictionary.
        """
        self.logger.info('Saving output')

        ################################################################################################################
        # Make all variables from config, input and modules available (note, config not available in output module, see Program.py)
        ################################################################################################################
        # read input file
        reader = Reader()
        reader.open(self.input.v('inputFile'))
        data = reader.read('module')
        reader.close()

        # merge the datacontainers of all modules & make the module tags into a list of modules
        inputvars = DataContainer()
        module = []
        for d in data:
            module.append(d.v('module'))
            inputvars.merge(d)
        inputvars.addData('module', module)

        # merge input vars with self.input in hierarchy config, input, input for this module, vars calculated in other modules (low - high)
        # + make a list of all keys of input and config vars; these are saved always and later appended by selected module calc. vars.
        data = self.__loadConfig()
        data.merge(inputvars)
        outputKeys = self.__checkInputOverrides(
            data
        )  # checks if input is overwritten and provides the keys of not or correctly overwritten input vars
        data.merge(self.input)
        del inputvars, reader
        # now all variables from config, input and modules are in 'data'

        ################################################################################################################
        # Isolate part of DC to write; put this in saveData
        ################################################################################################################
        saveData = DataContainer()

        # vars to save
        outputVariables = toList(self.input.v('requirements'))
        outputKeys = list(
            set(outputKeys + self.__getSubmoduleRequirements(outputVariables))
        )  # convert the requested output variables to key tuples including submodule requirements
        for key in outputKeys:
            if len(key) > 1:
                saveData.merge({key[0]: data.slice(*key).data})
            else:
                saveData.merge(data.slice(*key))

        # add grid and outputgrid if available; needed for interpolating data to outputgrid later
        saveData.merge(self.input.slice('grid'))
        saveData.merge(self.input.slice(self.outputgridName))

        # add reference level to outputgrid
        if self.input.v('R') is not None:
            self.input.merge({
                self.outputgridName: {
                    'low': {
                        'z':
                        self.input.v('R',
                                     x=self.input.v(self.outputgridName,
                                                    'axis', 'x'))
                    }
                }
            })  # add reference level to outputgrid

        # make a deepcopy of the data to be saved
        # NB. very memory inefficient, but needed not to overwrite existing data
        saveData = deepcopy(saveData)

        ################################################################################################################
        # Convert data using output grid (if this is provided)
        ################################################################################################################
        grid = saveData.slice('grid')
        outputgrid = saveData.slice(self.outputgridName)
        saveAnalytical = toList(self.input.v('saveAnalytical')) or []
        dontConvert = toList(self.input.v('dontConvert')) or []
        if 'all' in saveAnalytical:
            saveAnalytical = outputVariables
        if 'all' in dontConvert:
            dontConvert = outputVariables
        self._convertData(saveData, grid, outputgrid, saveAnalytical,
                          dontConvert)

        # rename the outputgrid to grid and replace the original grid in saveData
        saveData.addData('grid', saveData.data[self.outputgridName])
        saveData.data.pop(self.outputgridName)

        ################################################################################################################
        # Make the output directory if it doesnt exist
        ################################################################################################################
        cwdpath = cfm.CWD  # path to working directory
        self.path = os.path.join(cwdpath, self.input.v('path'))
        if not self.path[-1] == '/':
            self.path += '/'
        if not os.path.exists(self.path):
            os.makedirs(self.path)

        ################################################################################################################
        # set file name and save data
        ################################################################################################################
        filename = self.__makeFileName()

        # write
        filepath = (self.path + filename + self.ext)
        try:
            with open(filepath, 'wb') as fp:
                pickle.dump(saveData.data,
                            fp,
                            protocol=pickle.HIGHEST_PROTOCOL)
        except:
            raise

        ################################################################################################################
        # return
        ################################################################################################################
        d = {}
        d['outputDirectory'] = self.path
        return d