コード例 #1
0
class Component(object):
    """
    Abstract class defininig methods inherited by all CliMT components.
    """
    def __init__(self, **kwargs):

        # Initialize self.Fixed (subset of self.Prognostic which will NOT be time-marched)
        if 'Fixed' in kwargs: self.Fixed = kwargs.pop('Fixed')
        else: self.Fixed = []

        # Initialize I/O
        self.Io = IO(self, **kwargs)

        # Get values from restart file, if available
        if 'RestartFile' in kwargs:
            ParamNames = Parameters().value.keys()
            FieldNames = self.Required
            kwargs = self.Io.readRestart(FieldNames, ParamNames, kwargs)

        # Initialize scalar parameters
        self.Params = Parameters(**kwargs)

        # Frequency with which compute() will be executed
        if 'UpdateFreq' in kwargs:
            self.UpdateFreq = kwargs.pop('UpdateFreq')
        else:
            self.UpdateFreq = self.Params['dt']

        # Check if model can integrate
        if 'CanIntegrate' in kwargs:
            self.CanIntegrate = kwargs.pop('CanIntegrate')
            if (
                    'Integrates' in kwargs
            ) and self.CanIntegrate:  # this is a list of fields it can integrate
                self.Integrates = kwargs.pop('Integrates')
            else:
                raise IndexError, '\n\n CanIntegrate keyword must be accompanied by \
                        the Integrates keyword which provides a list of fields that this\
                        Component accepts for integration.'

        else:
            self.CanIntegrate = False

        # Initialize State
        self.State = State(self, **kwargs)
        self.Grid = self.State.Grid
        if 'grid' in kwargs: kwargs.pop('grid')

        # Dictionary to hold increments on prognos fields
        # We need three increments for a third order Adams-Bashforth
        self.Inc = {}
        self.IncOld = {}
        self.IncOlder = {}

        # Initialize diagnostics
        self.compute(ForcedCompute=True)

        # Create output file
        self.Io.createOutputFile(self.State, self.Params.value)

        # Write out initial state
        if not self.Io.Appending: self.write()

        # Initialize plotting facilities
        self.Plot = Plot()

        # Initialize runtime monitor
        self.Monitor = Monitor(self, **kwargs)

        # Notify user of unused input quantities
        self._checkUnused(kwargs)

        # Set some redundant attributes (mainly for backward compatibility)
        self.nlon = self.Grid['nlon']
        self.nlat = self.Grid['nlat']
        self.nlev = self.Grid['nlev']
        try:
            self.o3 = self.State['o3']
        except:
            pass

    def compute(self, ForcedCompute=False):
        """
        Updates component's diagnostics and increments
        """
        # If this component can integrate, no need for computing
        # increments
        if self.CanIntegrate:
            return

        # See if it's time for an update; if not, skip rest
        if not ForcedCompute:
            freq = self.UpdateFreq
            time = self.State.ElapsedTime
            if int(time / freq) == int((time - self['dt']) / freq): return

        # Set up union of State, Grid and Params
        Input = {}
        for dic in [self.State.Now, self.Grid.value, self.Params.value]:
            Input.update(dic)
        Input['UpdateFreq'] = self.UpdateFreq

        # For implicit time stepping, replace current time level with previous (old) time level
        if self.SteppingScheme == 'implicit': Input.update(self.State.Old)

        # For semimplicit time stepping, append previous (old) time level to Input dict
        if self.SteppingScheme == 'semi-implicit':
            for key in self.Prognostic:
                Input[key + 'old'] = self.State.Old[key]

        # List of arguments to be passed to extension
        args = [Input[key] for key in self.ToExtension]

        # Call extension and build dictionary of outputs
        OutputValues = self.driver(*args)
        if len(self.FromExtension) == 1:
            Output = {self.FromExtension[0]: OutputValues}
        else:
            Output = dict(zip(self.FromExtension, OutputValues))

        # Extract increments from Output
        for key in self.Prognostic:
            self.Inc[key] = Output.pop(key + 'inc')

        # Remove increments of Fixed variables
        for key in self.Fixed:
            if key in self.Inc: self.Inc.pop(key)
            if key in Output: Output.pop(key)

        # Update State
        self.State.update(Output)
        for key in Output:
            exec('self.' + key + '=Output[key]')

        # No further need for input dictionary
        del (Input)

    def step(self, RunLength=1, Inc={}):
        """
        Advances component one timestep and writes to output file if necessary.
        Inc is an externally-specified set of increments added to the internally-computed
        increments at each time step. 
        """

        # If RunLength is integer, interpret as number of time steps
        if type(RunLength) is type(1):
            NSteps = RunLength

        # If RunLength is float, interpret as length of run in seconds
        if type(RunLength) is type(1.):
            NSteps = int(RunLength / self['dt'])

        for i in range(NSteps):

            # Add external increments
            for key in Inc.keys():
                if key in self.Inc.keys():
                    self.Inc[key] += Inc[key]
                else:
                    self.Inc[key] = Inc[key]

        # If the component already has the capability to integrate,
        # no need to call the integrator in State.advance
            if self.CanIntegrate:
                Input = []
                InputTend = []
                for key in self.Integrates:

                    if key in self.State.Now:
                        Input.append(self.State.Now[key].copy())
                    else:
                        raise IndexError, '\n\n Required field ' + key + ' not present in State for integration'

                    temp = zeros(self.State.Now[key].shape)
                    if key in self.Inc:
                        temp = self.Inc.pop(key)

                    InputTend.append(temp)

                OutputValues = self.integrate(Input, InputTend)

                if len(self.FromExtension) == 1:
                    Output = {self.FromExtension[0]: OutputValues}
                else:
                    Output = dict(zip(self.FromExtension, OutputValues))

                for key in self.FromExtension:
                    self.State.Now[key] = Output[key]

            # Avance prognostics 1 time step
            self.State.advance(self)

            # Bring diagnostics and increments up to date
            self.compute()

            # Bring calendar up to date
            self['calday'] += self['dt'] / self['lod']
            if self['calday'] > self['daysperyear']:
                self['calday'] -= self['daysperyear']

            # Write to file, if it's time to
            dt = self.Params['dt']
            time = self.State.ElapsedTime
            freq = self.Io.OutputFreq
            if int(time / freq) != int((time - dt) / freq):
                self.write()

            # Refresh monitor, if it's time to
            if self.Monitor.Monitoring:
                freq = self.Monitor.MonitorFreq
                if int(time / freq) != int((time - dt) / freq):
                    self.Monitor.refresh(self)

    def __call__(self, **kwargs):
        """
        # Provides a simple interface to extension, useful e.g. for diagnostics. 
        """
        # Re-initialize parameters, grid and state
        self.Params = Parameters(**kwargs)
        self.State = State(self, **kwargs)
        self.Grid = self.State.Grid
        # Bring diagnostics up to date
        self.compute()

    def write(self):
        """
        Invokes write method of IO instance to write out current State
        """
        self.Io.writeOutput(self.Params, self.State)

    def open(self, OutputFileName='CliMT.nc'):
        """
        """
        if self.Io.OutputFileName == OutputFileName:
            print '\n +++ ClimT.Io: File %s is currently open for output' % OutputFileName
            return
        else:
            print 'Opening %s for output' % OutputFileName
            self.Io.OutputFileName = OutputFileName
            self.Io.DoingOutput = True
            self.Io.Appending = False
            self.Io.OutputTimeIndex = 0
            self.Io.createOutputFile(self.State, self.Params)

    def plot(self, *FieldKeys):
        self.Plot(self, *FieldKeys)

    def setFigure(self, FigureNumber=None):
        self.Plot.setFigure(FigureNumber)

    def closeFigure(self, FigureNumber=None):
        self.Plot.closeFigure(FigureNumber)

    def usage(self):
        print self.__doc__

    def report(self):
        print 'CliMT component:\n    %s' % self.Name
        keys = self.State.keys()
        keys1 = []
        for i in range(len(keys)):
            if keys[i] in self.Prognostic:
                keys1.append('%12s   %s' % (keys[i], '(prognostic)'))
        for i in range(len(keys)):
            if keys[i] in self.Diagnostic:
                keys1.append('%12s   %s' % (keys[i], '(diagnostic)'))
        for i in range(len(keys)):
            if keys[i] not in self.Prognostic and keys[
                    i] not in self.Diagnostic:
                keys1.append('%12s   %s' % (keys[i], '(Fixed)'))
        print 'State variables:\n %s' % '\n '.join(keys1)

    def _checkUnused(self, kwargs):
        '''
        Notify of unused input quantities.
        '''
        unused = []
        io_keys = [
            'RestartFile', 'OutputFile', 'OutputFreq', 'OutputFields',
            'ElapsedTime'
        ]
        monitor_keys = ['MonitorFields', 'MonitorFreq']
        for key in kwargs:
            if key not in self.Params  \
            and key not in self.Grid   \
            and key not in KnownFields \
            and key not in io_keys \
            and key not in monitor_keys:
                unused.append(key)

        if len(unused) > 0:
            if len(unused) == 1: suffix = 'y'
            else: suffix = 'ies'
            print '\n ++++ CliMT.'+self.Name+'.initialize: WARNING: Input quantit%s %s not used.\n' \
                   % (suffix,str(list(unused)))

    def _getShape3D(self, **kwargs):
        '''
        Returns shape of 3D arrays to be passed to extension.
        '''
        return (self._getAxisLength('lon', **kwargs),
                self._getAxisLength('lat', **kwargs),
                self._getAxisLength('lev', **kwargs))

    def getGrid(self):
        '''
        Returns Grid shape for external purposes, after init
        '''

        return self.Grid

    def _getAxisLength(self, AxisName, **kwargs):
        '''
        Returns length of axis.
        '''
        # Check input
        assert AxisName in ['lev','lat','lon'], \
               '\n\n ++++ CliMT.%s: Axis name must be one of "lon", "lat", "lev"' % self.Name

        # See if axis was supplied in input
        n = None
        if AxisName in kwargs:
            if array(kwargs[AxisName]).ndim == 0:
                n = 1
            else:
                assert array(kwargs[AxisName]).ndim == 1, \
                    '\n\n ++++ CliMT.%s.init: input %s must be rank 1' % (self.Name,AxisName)
                n = len(array(kwargs[AxisName]))

        # If not, see if some field was supplied
        else:
            for key in kwargs:
                if key in KnownFields:
                    if KnownFields[key][2] == '2D' and AxisName != 'lev':
                        i = ['lon', 'lat'].index(AxisName)
                        try:
                            n = array(kwargs[key]).shape[i]
                        except:
                            n = 1
                    elif KnownFields[key][2] == '3D':
                        i = ['lon', 'lat', 'lev'].index(AxisName)
                        try:
                            n = array(kwargs[key]).shape[i]
                        except:
                            n = 1

        # Last resort: get dimensions set in Makefile
        if n is None: exec('n = get_n%s()' % AxisName)

        # Check if extension enforces axis dimension, ensure consistency
        try:
            exec('n_ext = self.Extension.get_n%s()' % AxisName)
        except:
            n_ext = n
        assert n_ext == n, \
            '\n\n ++++ CliMT.%s.init: input %s has dimension %i but extension requires %i'% \
            (self.Name, AxisName, n, n_ext)

        return n

    # Returns requested quantity from Params, Grid or State
    def __getitem__(self, key):
        for obj in [self.Params, self.Grid, self.State]:
            if key in obj:
                if isinstance(obj[key], basestring): return obj[key]
                else: return squeeze(obj[key]).copy(order='F')
        raise IndexError, '\n\n CliMT.State: %s not in Params, Grid or State' % str(
            key)

    # Sets requested quantity in Params, Grid or State
    def __setitem__(self, key, value):
        if key in self.Params:
            self.Params[key] = value
            return
        if key in self.Grid:
            self.Grid[key] = value
            return
        elif key in self.State and KnownFields[key][2] == '2D':
            self.State[key] = reshape(value, self.Grid.Shape3D[1:3])
            return
        elif key in self.State and KnownFields[key][2] == '3D':
            self.State[key] = reshape(value, self.Grid.Shape3D)
            return
        else:
            raise IndexError, '\n\n CliMT.State: %s not in Params, Grid or State' % str(
                key)
コード例 #2
0
class federation(Component):
    """
    Combine components to create time-dependent model.

    * Instantiation:
    
      x = climt.federation(C1, C2, ..., Cn, <args> )

      where C1, ..., Cn are instances of CliMT components 
      and <args> are any keywork arguments relevant to the
      components included.

      The parameters and state of constituent components is re-initialized.

    * Running the federation:

      x.step()     will evolve the federation 1 timestep
      x.step(100)  will evolve the federation 100 timesteps
      x.step(100.) will evolve the federation 100 seconds
    """
    def __init__(self, *components, **kwargs):
        """
        """
        # Check input components
        if len(components) < 2:
            raise \
        '\n\n +++ CliMT.federation: you must give me more than 1 component to federate!\n\n'
        else:
            for component in components:
                assert isinstance(component, Component), \
                '\n\n +++CliMT.federation: Input item %s is not an instance.\n\n' % str(component)

        # Re-order components: diagnostic, semi-implicit, explicit, implicit
        components = list(components)
        """
        for i in range(len(components)):
            if len(components[i].Prognostic) > 0:
                components.append(components.pop(i))
        for scheme in ['semi-implicit', 'explicit', 'implicit']:
            for i in range(len(components)):
                if components[i].SteppingScheme == scheme:
                    components.append(components.pop(i))
        """
        self.components = components

        # Federation's Required is union of all components' Required;
        # same for Prognostic and Diagnostic
        self.Required = []
        self.Prognostic = []
        self.Diagnostic = []
        for component in components:
            self.Required = list(set(self.Required).union(component.Required))
            self.Prognostic = list(
                set(self.Prognostic).union(component.Prognostic))
            self.Diagnostic = list(
                set(self.Diagnostic).union(component.Diagnostic))

        #Check if any components carry an integrator
        self.Integrators = []
        self.Integrates = []
        self.FromExtension = []
        self.CanIntegrate = False
        for component in components:
            if component.CanIntegrate:
                self.CanIntegrate = component.CanIntegrate

                common_fields = set(component.Integrates).intersection(
                    self.Integrates)
                if common_fields:
                    # Two components are trying to integrate the same field.
                    # don't allow
                    raise IndexError, "\n\n Two components are trying to integrate the fields ",\
                                                     common_fields

                for field in component.Integrates:
                    self.Integrates.append(field)

                for field in component.FromExtension:
                    self.FromExtension.append(field)

                self.Integrators.append(component)
                print component.Name, ' can integrate ', component.Integrates

        if self.CanIntegrate:
            print 'All fields integrated by federation members: ', self.Integrates
            print 'All fields returned by federation members: ', self.FromExtension

        # Other attributes
        self.Name = 'federation'
        self.Extension = None

        # Set LevType to None if all components are None, else p
        self.LevType = None
        for component in components:
            if component.LevType == 'p': self.LevType = 'p'

        # Initialize self.Fixed (subset of self.Prognostic which will NOT be time-marched)
        if 'Fixed' in kwargs: self.Fixed = kwargs.pop('Fixed')
        else: self.Fixed = []

        # Instantiate I/O
        self.Io = IO(self, **kwargs)

        # Get values from restart file, if available
        if 'RestartFile' in kwargs:
            ParamNames = Parameters().value.keys()
            FieldNames = self.Required
            kwargs = self.Io.readRestart(FieldNames, ParamNames, kwargs)

        # Initialize scalar parameters
        self.Params = Parameters(**kwargs)

        # Initialize State
        self.State = State(self, **kwargs)
        self.Grid = self.State.Grid
        if 'grid' in kwargs:
            self.Grid = kwargs.pop('grid')

        # Set some redundant attributes (mainly for backward compatibility)
        self.nlon = self.Grid['nlon']
        self.nlat = self.Grid['nlat']
        self.nlev = self.Grid['nlev']
        try:
            self.o3 = self.State['o3']
        except:
            pass

        self.componentGrids = []
        # Check if components enforce axis dimensions, ensure consistency
        for component in self.components:
            if component.Grid not in self.componentGrids:
                self.componentGrids.append(component.Grid)
            for AxisName in ['lev', 'lat', 'lon']:
                exec('n_fed = self.n%s' % AxisName)
                try:
                    exec('n_com = component.Extension.get_n%s()' % AxisName)
                except:
                    n_com = n_fed
                assert n_com == n_fed, \
                '\n\n ++++ CliMT.federation.init: recompile with %i %ss to run this federation\n'\
                % (n_fed,AxisName)

        # Dictionary to hold increments on prognos fields
        # We need three increments for a third order Adams-Bashforth
        self.Inc = {}
        self.IncOld = {}
        self.IncOlder = {}

        # Adjust components' attributes
        for component in self.components:
            component.Monitoring = False
            component.Io.OutputFreq = self.Io.OutputFreq
            component.Fixed.extend(self.Fixed)
            if component.UpdateFreq == component['dt']:
                component.UpdateFreq = self['dt']
            component.Params = self.Params
            component.Grid = self.State.Grid
            component.State = self.State
            component.Inc = {}
            # insolation component gets special treatment because
            # of need to set orb params in common block (yes, this is ugly)
            try:
                component.setOrbParams(**kwargs)
            except:
                pass
        self.compute(ForcedCompute=True)

        # Create output file
        self.Io.createOutputFile(self.State, self.Params.value)

        # Write out initial state
        if not self.Io.Appending: self.write()

        # Initialize plotting facilities
        self.Plot = Plot()

        # Initialize runtime monitor
        self.Monitor = Monitor(self, **kwargs)

        # Notify user of unused input quantities
        self._checkUnused(kwargs)

        # Print out report
        #self.report()

    def compute(self, ForcedCompute=False):
        """
        Update federation's diagnostics and increments.
        """
        ## New = self.State.Old.copy()
        ## for component in self.components:
        ##     # enforce time-splitting of implicit and semi-implicit components
        ##     self.State.Old.update(New)
        ##     # bring component's diagnostics and increments up to date
        ##     component.compute(ForcedCompute=ForcedCompute)
        ##     # accumulate increments
        ##     for key in component.Inc:
        ##         New[key] += component.Inc[key]
        ## for key in self.State.Old:
        ##     self.Inc[key] = New[key]  - self.State.Old[key]

        self.Inc = self.State.Old.copy()
        for key in self.Inc:
            self.Inc[key] = self.Inc[key] * 0.
        for component in self.components:
            # bring component's diagnostics and increments up to date
            component.compute(ForcedCompute=ForcedCompute)
            # accumulate increments
            for key in component.Inc:
                self.Inc[key] += component.Inc[key]

    def integrate(self, field_list, increment_list):

        Input = []
        InputTend = []
        Output = list(self.FromExtension)

        for component in self.Integrators:

            #print 'In Fed: ', component.Integrates

            for field in component.Integrates:

                index = self.Integrates.index(field)

                Input.append(field_list[index])
                InputTend.append(increment_list[index])

            OutputValues = component.integrate(Input, InputTend)

            for field in component.FromExtension:

                fed_index = self.FromExtension.index(field)
                comp_index = component.FromExtension.index(field)
                #print 'In Fed: ', field, fed_index, comp_index

                Output[fed_index] = OutputValues[comp_index]

        return Output