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)
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)