Esempio n. 1
0
    def run(self, t_end, dt, max_steps=1000, solver="odeint", output="dataframes",
            terminate=False):
        """Run the parcel model.

        After initializing the parcel model, it can be immediately run by
        calling this function. Before the model is integrated, a routine
        :func:`_setup_run()` is performed to equilibrate the initial aerosol
        population to the ambient meteorology if it hasn't already been done.
        Then, the initial conditions are
        passed to a user-specified solver which integrates the system forward
        in time. By default, the integrator wraps ODEPACK's LSODA routine through
        SciPy's :func:`odeint` method, but extensions to use other solvers can be
        written easily (for instance, two methods from :mod:`odespy` are given
        below).

        The user can specify the timesteps to evaluate the trace of the parcel
        in one of two ways:

            #. Setting ``dt`` will automatically specify a timestep to use and \
                the model will use it to automatically populate the array to \
                pass to the solver.
            #. Otherwise, the user can specify ``ts`` as a list or array of the \
                timesteps where the model should be evaluated.

        **Args**:
            * *z_top* -- Vertical extent to which the model should be integrated.
            * *dt* -- Timestep intervals to report model output.
            * *ts* -- Pre-computed array of timestamps where output is requested.
            * *max_steps* -- Maximum number of steps allowed by solver to achieve \
                tolerance during integration.
            * *solver* -- A string indicating which solver to use:
                * ``"odeint"``: LSODA implementation from ODEPACK via \
                    SciPy's ``integrate`` module
                * ``"lsoda"``: LSODA implementation from ODEPACK via odespy
                * ``"lsode"``: LSODE implementation from ODEPACK via odespy
            * *output* -- A string indicating the format in which the output of the run should be returned

        **Returns**:
            Depending on what was passed to the *output* argument, different
            types of data might be returned:
                * ``"dataframes"``: (default) will process the output into \
                    two pandas DataFrames - the first one containing profiles \
                    of the meteorological quantities tracked in the model, \
                    and the second a dictionary of DataFrames with one for \
                    each AerosolSpecies, tracking the growth in each bin \
                    for those species.
                * ``"arrays"``: will return the raw output from the solver \
                    used internally by the parcel model - the state vector \
                    ``x`` and the evaluated timesteps converted into height \
                    coordinates.
                * ``"smax"``: will only return the maximum supersaturation \
                    value achieved in the simulation.

            A tuple whose first element is a DataFrame containing the profiles
            of the meteorological quantities tracked in the parcel model, and
            whose second element is a dictionary of DataFrames corresponding to
            each aerosol species and their droplet sizes.

        **Raises**:
            ``ParcelModelError``: The parcel model failed to complete successfully or failed to initialize.

        """
        if not output in ["dataframes", "arrays", "smax"]:
            raise ParcelModelError("Invalid value ('%s') specified for output format." % output)

        if not self._model_set:
            _ = self._setup_run()

        y0 = self.y0
        r_drys = self._r_drys
        kappas = self._kappas
        Nis = self._Nis
        nr = self._nr

        ## Setup run time conditions
        t = np.arange(0., t_end+dt, dt)
        if self.console:
            print "\n"+"n_steps = %d" % (len(t))+"\n"

        ## Setup/run integrator
        try:
            from parcel_aux import der as der_fcn
        except ImportError:
            print "Could not load Cython derivative; using Python version."
            from parcel import der as der_fcn

        args = (nr, r_drys, Nis, self.V, kappas)
        integrator = Integrator.solver(solver)

        ## Is the updraft speed a function?
        v_is_func = hasattr(self.V, '__call__')
        if v_is_func: # Re-wrap the function to correctly figure out V            
            orig_der_fcn = der_fcn
            def der_fcn(y, t, nr, r_drys, Nis, V, kappas):
                V_t = self.V(t)
                return orig_der_fcn(y, t, nr, r_drys, Nis, V_t, kappas)

        try:
            x, t, success = integrator(der_fcn, t, y0, args, self.console, max_steps, terminate)
        except ValueError, e:
            raise ParcelModelError("Something failed during model integration: %r" % e)
Esempio n. 2
0
    def run(self, t_end, 
            output_dt=1., solver_dt=None, 
            max_steps=1000, solver="odeint", output="dataframes",
            terminate=False, terminate_depth=100., **solver_args):
        """ Run the parcel model simulation.

        Once the model has been instantiated, a simulation can immediately be 
        performed by invoking this method. The numerical details underlying the 
        simulation and the times over which to integrate can be flexibly set 
        here.

        **Time** -- The user must specify two timesteps: `output_dt`, which is the 
        timestep between output snapshots of the state of the parcel model, and 
        `solver_dt`, which is the the interval of time before the ODE integrator
        is paused and re-started. It's usually okay to use a very large `solver_dt`,
        as `output_dt` can be interpolated from the simulation. In some cases though
        a small `solver_dt` could be useful to force the solver to use smaller 
        internal timesteps.

        **Numerical Solver** -- By default, the model will use the `odeint` wrapper
        of LSODA shipped by default with scipy. Some fine-tuning of the solver tolerances
        is afforded here through the `max_steps`. For other solvers, a set of optional
        arguments `solver_args` can be passed. 

        **Solution Output** -- Several different output formats are available by default. 
        Additionally, the output arrays are saved with the `ParcelModel` instance so they
        can be used later.

        Parameters
        ----------
        t_end : float
            Total time over interval over which the model should be integrated
        output_dt : float 
            Timestep intervals to report model output.
        solver_dt : float
            Timestep interval for calling solver integration routine.
        max_steps : int 
            Maximum number of steps allowed by solver to satisfy error tolerances
            per timestep.
        solver : {'odeint', 'lsoda', 'lsode', 'vode', cvode'}
            Choose which numerical solver to use:
            * `'odeint'`: LSODA implementation from ODEPACK via 
                SciPy's integrate module
            * `'lsoda'`: LSODA implementation from ODEPACK via odespy
            * `'lsode'`: LSODE implementation from ODEPACK via odespy
            * `'vode'` : VODE implementation from ODEPACK via odespy
            * `'cvode'` : CVODE implementation from Sundials via Assimulo
            * `'lsodar'` : LSODAR implementation from Sundials via Assimulo
        output : {'dataframes', 'arrays', 'smax'}
            Choose format of solution output.
        terminate : boolean
            End simulation at or shortly after a maximum supersaturation has been achieved
        terminate_depth : float, optional (default=100.)
            Additional depth (in meters) to integrate after termination criterion
            eached.

        Returns
        -------
        DataFrames, array, or float
            Depending on what was passed to the *output* argument, different
            types of data might be returned:

            - `dataframes': (default) will process the output into
               two pandas DataFrames - the first one containing profiles
               of the meteorological quantities tracked in the model,
               and the second a dictionary of DataFrames with one for
               each AerosolSpecies, tracking the growth in each bin
               for those species.
            - 'arrays': will return the raw output from the solver
               used internally by the parcel model - the state vector
               `y` and the evaluated timesteps converted into height
               coordinates.
            - 'smax': will only return the maximum supersaturation
               value achieved in the simulation.

        Raises
        ------
        ParcelModelError 
            The parcel model failed to complete successfully or failed to initialize.

        See Also
        --------
        der : right-hand side derivative evaluated during model integration.
        
        """
        if not output in ["dataframes", "arrays", "smax"]:
            raise ParcelModelError("Invalid value ('%s') specified for output format." % output)

        if solver_dt is None:
            solver_dt = 10.*output_dt

        if not self._model_set:
            self._setup_run()

        y0 = self.y0
        r_drys = self._r_drys
        kappas = self._kappas
        Nis = self._Nis
        nr = self._nr

        ## Setup/run integrator
        try:
            from parcel_aux import der as der_fcn
        except ImportError:
            print "Could not load Cython derivative; using Python version."
            from parcel import der as der_fcn

        ## Is the updraft speed a function of time?
        v_is_func = hasattr(self.V, '__call__')
        if v_is_func: # Re-wrap the function to correctly figure out V            
            orig_der_fcn = der_fcn
            def der_fcn(y, t, *args):
                V_t = self.V(t)
                args[3] = V_t
                return orig_der_fcn(y, t, *args)

        ## Will the simulation terminate early?
        if not terminate:
            terminate_depth = 0.
        else:
            if terminate_depth <= 0.:
                raise ParcelModelError("`terminate_depth` must be greater than 0!")

        if self.console:
            print
            print "Integration control"
            print "----------------------------"
            print "        output dt: ", output_dt
            print "    max solver dt: ", solver_dt
            print " solver int steps: ", int(solver_dt/output_dt )
            print "      termination: %r (%5dm)" % (terminate, terminate_depth)

        args = [nr, r_drys, Nis, self.V, kappas, self.accom]
        integrator_type = Integrator.solver(solver)
        integrator = integrator_type(der_fcn, output_dt, solver_dt, y0, args, 
                                     terminate=terminate, terminate_depth=terminate_depth,
                                     console=self.console,
                                     **solver_args)

        try:
            ## Pack args as tuple for solvers
            args = tuple(args)

            if self.console: print "\nBEGIN INTEGRATION ->\n"
            x, t, success = integrator.integrate(t_end)
        except ValueError, e:
            raise ParcelModelError("Something failed during model integration: %r" % e)