Ejemplo n.º 1
0
def odeint(func, y0, tspan, args=(), Dfun=None, col_deriv=0,
    full_output=0, ml=None, mu=None, rtol=None, atol=None, tcrit=None,
    h0=0.0, hmax=0.0, hmin=0.0, ixpr=0, mxstep=0, mxhnil=0, mxordn=12,
    mxords=5, printmessg=0):
    'Wrapped scipy.integrate.odeint with units'

    def wrappedfunc(y, t, *args):
        y = Unit(y, y0.exponents, y0.label)
        t = Unit(tspan, tspan.exponents, tspan.label)
        return float(func(y, t, *args))

    if full_output:
        y, infodict = _odeint(wrappedfunc, y0, tspan, args=(),
                              Dfun=None, col_deriv=0, full_output=0,
                              ml=None, mu=None, rtol=None, atol=None,
                              tcrit=None, h0=0.0, hmax=0.0, hmin=0.0,
                              ixpr=0, mxstep=0, mxhnil=0, mxordn=12,
                              mxords=5, printmessg=0)
    else:
        y = _odeint(wrappedfunc, y0, tspan, args=(),
                              Dfun=None, col_deriv=0, full_output=0,
                              ml=None, mu=None, rtol=None, atol=None,
                              tcrit=None, h0=0.0, hmax=0.0, hmin=0.0,
                              ixpr=0, mxstep=0, mxhnil=0, mxordn=12,
                              mxords=5, printmessg=0)
    y = Unit(y, y0.exponents, y0.label)
    return y
Ejemplo n.º 2
0
def odeint(func,
           y0,
           t,
           args=(),
           Dfun=None,
           col_deriv=0,
           full_output=0,
           ml=None,
           mu=None,
           rtol=None,
           atol=None,
           tcrit=None,
           h0=0.0,
           hmax=0.0,
           hmin=0.0,
           ixpr=0,
           mxstep=0,
           mxhnil=0,
           mxordn=12,
           mxords=5,
           printmessg=0):
    """wrapper for scipy.integrate.odeint to work with quantities."""
    def wrapped_func(Y0, T, *args):
        # put units on T if they are on the original t
        # check for units so we don't put them on twice
        if not hasattr(T, 'units') and hasattr(t, 'units'):
            T = T * t.units

        # now for the dependent variable units. Y0 may be a scalar or
        # a list or an array. we want to check each element of y0 for
        # units, and add them to the corresponding element of Y0 if we
        # need to.
        try:
            uY0 = [x for x in Y0]  # a list copy of contents of Y0
            # this works if y0 is an iterable, eg. a list or array
            for i, yi in enumerate(y0):
                if not hasattr(uY0[i], 'units') and hasattr(yi, 'units'):

                    uY0[i] = uY0[i] * yi.units

        except TypeError:
            # we have a scalar
            if not hasattr(Y0, 'units') and hasattr(y0, 'units'):
                uY0 = Y0 * y0.units

        # It is necessary to rescale this to prevent issues with non-simplified
        # units.
        val = func(uY0, t, *args).rescale(y0.units / t.units)

        try:
            return np.array([float(x) for x in val])
        except TypeError:
            return float(val)

    if full_output:
        y, infodict = _odeint(wrapped_func, y0, t, args, Dfun, col_deriv,
                              full_output, ml, mu, rtol, atol, tcrit, h0, hmax,
                              hmin, ixpr, mxstep, mxhnil, mxordn, mxords,
                              printmessg)
    else:
        y = _odeint(wrapped_func, y0, t, args, Dfun, col_deriv, full_output,
                    ml, mu, rtol, atol, tcrit, h0, hmax, hmin, ixpr, mxstep,
                    mxhnil, mxordn, mxords, printmessg)

    # now we need to put units onto the solution units should be the
    # same as y0. We cannot put mixed units in an array, so, we return a list
    m, n = y.shape  # y is an ndarray, so it has a shape
    if n > 1:  # more than one equation, we need a list
        uY = [0 for yi in range(n)]

        for i, yi in enumerate(y0):
            if not hasattr(uY[i], 'units') and hasattr(yi, 'units'):
                uY[i] = y[:, i] * yi.units
            else:
                uY[i] = y[:, i]

    else:
        uY = y * y0.units

    y = uY

    if full_output:
        return y, infodict
    else:
        return y
Ejemplo n.º 3
0
Archivo: pysd.py Proyecto: arunbte/pysd
    def run(self, params={}, return_columns=[], return_timestamps=[],
            initial_condition='original', collect=False, **intg_kwargs):
        """ Simulate the model's behavior over time.
        Return a pandas dataframe with timestamps as rows,
        model elements as columns.

        Parameters
        ----------

        params : dictionary
            Keys are strings of model component names.
            Values are numeric or pandas Series.
            Numeric values represent constants over the model integration.
            Timeseries will be interpolated to give time-varying input.

        return_timestamps : list, numeric, numpy array(1-D)
            Timestamps in model execution at which to return state information.
            Defaults to model-file specified timesteps.

        return_columns : list of string model component names
            Returned dataframe will have corresponding columns.
            Defaults to model stock values.

        initial_condition : 'original'/'o', 'current'/'c', (t, {state})
            The starting time, and the state of the system (the values of all the stocks)
            at that starting time.

            * 'original' (default) uses model-file specified initial condition
            * 'current' uses the state of the model after the previous execution
            * (t, {state}) lets the user specify a starting time and (possibly partial)
              list of stock values.

        collect: binary (T/F)
            When running multiple simulations, collect the results in a way
            that we can access down the road.

        intg_kwargs: keyword arguments for odeint
            Provides precice control over the integrator by passing through
            keyword arguments to scipy's odeint function. The most interesting
            of these will be `tcrit`, `hmax`, `mxstep`.


        Examples
        --------

        >>> model.run(params={'exogenous_constant':42})
        >>> model.run(params={'exogenous_variable':timeseries_input})
        >>> model.run(return_timestamps=[1,2,3.1415,4,10])
        >>> model.run(return_timestamps=10)
        >>> model.run(return_timestamps=np.linspace(1,10,20))

        See Also
        --------
        pysd.set_components : handles setting model parameters
        pysd.set_initial_condition : handles setting initial conditions

        """

        #if not self.components._stocknames:
            #raise RuntimeError('Cannnot integrate no-stock models.')

        if params:
            self.set_components(params)

        if initial_condition != 'current':
            self.set_initial_condition(initial_condition)

        tseries = self._build_timeseries(return_timestamps)

        # the odeint expects the first timestamp in the tseries to be the initial condition,
        # so we may need to add the t0 if it is not present in the tseries array
        addtflag = tseries[0] != self.components.t
        if addtflag:
            tseries = np.insert(tseries, 0, self.components.t)

        if self.components._stocknames:
            res = _odeint(func=self.components.d_dt,
                          y0=self.components.state_vector(),
                          t=tseries,
                          **intg_kwargs)
                          #hmax=self.components.time_step())

            state_df = _pd.DataFrame(data=res,
                                     index=tseries,
                                     columns=self.components._stocknames)
        else:
            state_df = _pd.DataFrame(index=tseries, data=1, columns=['dummy'])

        return_df = self.extend_dataframe(state_df, return_columns) if return_columns else state_df

        if addtflag:
            return_df.drop(return_df.index[0], inplace=True)

        if collect:
            self.record.append(return_df) #we could just record the state, and expand it later...

        # The integrator takes us past the last point in the tseries.
        # Go back to it, in order to maintain the state at a predictable location.
        # This may take up more time than we're willing to spend...
        if self.components.t != tseries[-1]:
            self.set_state(tseries[-1], dict(state_df.iloc[-1]))

        return return_df
Ejemplo n.º 4
0
    def run(self, params={}, return_columns=[], return_timestamps=[], initial_condition='original',
            collect=False):
        """
            Runs the model from the initial time all the way through the
            final time, returning a pandas dataframe of results.
            
            If ::return_timestamps:: is set, the dataframe will have these as
            its index. Otherwise, the index will be every timestep from the start
            of simulation to the finish.
            
            if ::return_columns:: is set, the dataframe will have these columns.
            Otherwise, it will return the value of the stocks.
            
            If ::params:: is set, modifies the model according to parameters
            before execution.
            
            format for params should be:
            params={'parameter1':value, 'parameter2':value}
            
            initial_condition can take a variety of values:
            - None or 'original' reinitializes the model at the initial conditions 
                 specified in the model file, and resets the simulation time accordingly
            - 'current' preserves the current state of the model, including the simulaion time
            - a tuple (t, dict) sets the simulation time to t, and the
            """
        
        ### Todo:
        #
        # - check that the return_timestamps is a collection - a list of timestamps, or something.
        #      if not(ie, a single value), make it one.
        
        if params:
            self.set_components(params)
        
        
        ##### Setting timestamp options
        if return_timestamps:
            tseries = return_timestamps
        else:
            tseries = np.arange(self.components.initial_time(),
                                self.components.final_time(),
                                self.components.time_step())
        
        ##### Setting initial condition options
        if isinstance(initial_condition, tuple):
            self.components.t = initial_condition[0] #this is brittle!
            self.components.state.update(initial_condition[1]) #this is also brittle. Assumes that the dictionary passed in has valid values
        elif isinstance(initial_condition, str):
            if initial_condition.lower() in ['original','o']:
                self.components.reset_state() #resets the state of the system to what the model contains (but not the parameters)
            elif initial_condition.lower() in ['current', 'c']:
                pass #placeholder - this is a valid option, but we don't modify anything
            else:
                assert False #we should throw an error if there is an unrecognized option
        else:
            assert False

        initial_values = self.components.state_vector()

        addtflag = False
        if tseries[0] != self.components.t:
            tseries = [self.components.t]+tseries
            addtflag = True

        ######Setting integrator options:
        #
        # - we may choose to use the odeint parameter ::tcrit::, which identifies singularities
        # near which the integrator should exercise additional caution.
        # we could get these values from any time-type parameters passed to a step/pulse type function
        #
        # - there may be a better way to use the integrator that lets us report additional values at
        # different timestamps. We may also want to use pydstool.
        #
        # the odeint expects the first timestamp in the tseries to be the initial condition,
        # so we may need to add the t0 if it is not present in the tseries array
        res = _odeint(self.components.d_dt, initial_values, tseries, hmax=self.components.time_step())
        
        stocknames = sorted(self.components.state.keys())
        values = _pd.DataFrame(data=res, index=tseries, columns=stocknames)


        if return_columns:
            # this is a super slow, super bad way to do this. Recode ASAP
            out = []
            for t, row in zip(values.index, values.to_dict(orient='records')):
                self.components.state.update(row)
                self.components.t = t
        
                for column in return_columns:
                    func = getattr(self.components, column)
                    row[column] = func()
        
                out.append(row)
        
            values = _pd.DataFrame(data=out, index=values.index)[return_columns] #this is ugly

        if addtflag:
            values = values.iloc[1:] #there is probably a faster way to drop the initial row

        if collect:
            self.record.append(values)
        
        return values
Ejemplo n.º 5
0
def odeint(func, y0, t, args=(),
           Dfun=None, col_deriv=0, full_output=0,
           ml=None, mu=None, rtol=None, atol=None,
           tcrit=None, h0=0.0, hmax=0.0, hmin=0.0,
           ixpr=0, mxstep=0, mxhnil=0, mxordn=12,
           mxords=5, printmessg=0):
    """wrapper for scipy.integrate.odeint to work with quantities."""

    def wrapped_func(Y0, T, *args):
        # put units on T if they are on the original t
        # check for units so we don't put them on twice
        if not hasattr(T, 'units') and hasattr(t, 'units'):
            T = T * t.units

        # now for the dependent variable units. Y0 may be a scalar or
        # a list or an array. we want to check each element of y0 for
        # units, and add them to the corresponding element of Y0 if we
        # need to.
        try:
            uY0 = [x for x in Y0]  # a list copy of contents of Y0
            # this works if y0 is an iterable, eg. a list or array
            for i, yi in enumerate(y0):
                if not hasattr(uY0[i], 'units') and hasattr(yi, 'units'):

                    uY0[i] = uY0[i] * yi.units

        except TypeError:
            # we have a scalar
            if not hasattr(Y0, 'units') and hasattr(y0, 'units'):
                uY0 = Y0 * y0.units

        # It is necessary to rescale this to prevent issues with non-simplified
        # units.
        val = func(uY0, t, *args).rescale(y0.units / t.units)

        try:
            return np.array([float(x) for x in val])
        except TypeError:
            return float(val)

    if full_output:
        y, infodict = _odeint(wrapped_func, y0, t, args,
                              Dfun, col_deriv, full_output,
                              ml, mu, rtol, atol,
                              tcrit, h0, hmax, hmin,
                              ixpr, mxstep, mxhnil, mxordn,
                              mxords, printmessg)
    else:
        y = _odeint(wrapped_func, y0, t, args,
                    Dfun, col_deriv, full_output,
                    ml, mu, rtol, atol,
                    tcrit, h0, hmax, hmin,
                    ixpr, mxstep, mxhnil, mxordn,
                    mxords, printmessg)

    # now we need to put units onto the solution units should be the
    # same as y0. We cannot put mixed units in an array, so, we return a list
    m, n = y.shape  # y is an ndarray, so it has a shape
    if n > 1:  # more than one equation, we need a list
        uY = [0 for yi in range(n)]

        for i, yi in enumerate(y0):
            if not hasattr(uY[i], 'units') and hasattr(yi, 'units'):
                uY[i] = y[:, i] * yi.units
            else:
                uY[i] = y[:, i]

    else:
        uY = y * y0.units

    y = uY

    if full_output:
        return y, infodict
    else:
        return y
Ejemplo n.º 6
0
    def run(self,
            params={},
            return_columns=[],
            return_timestamps=[],
            initial_condition='original',
            collect=False,
            **intg_kwargs):
        """ Simulate the model's behavior over time.
        Return a pandas dataframe with timestamps as rows,
        model elements as columns.

        Parameters
        ----------

        params : dictionary
            Keys are strings of model component names.
            Values are numeric or pandas Series.
            Numeric values represent constants over the model integration.
            Timeseries will be interpolated to give time-varying input.

        return_timestamps : list, numeric, numpy array(1-D)
            Timestamps in model execution at which to return state information.
            Defaults to model-file specified timesteps.

        return_columns : list of string model component names
            Returned dataframe will have corresponding columns.
            Defaults to model stock values.

        initial_condition : 'original'/'o', 'current'/'c', (t, {state})
            The starting time, and the state of the system (the values of all the stocks)
            at that starting time.

            * 'original' (default) uses model-file specified initial condition
            * 'current' uses the state of the model after the previous execution
            * (t, {state}) lets the user specify a starting time and (possibly partial)
              list of stock values.

        collect: binary (T/F)
            When running multiple simulations, collect the results in a way
            that we can access down the road.

        intg_kwargs: keyword arguments for odeint
            Provides precice control over the integrator by passing through
            keyword arguments to scipy's odeint function. The most interesting
            of these will be `tcrit`, `hmax`, `mxstep`.


        Examples
        --------

        >>> model.run(params={'exogenous_constant':42})
        >>> model.run(params={'exogenous_variable':timeseries_input})
        >>> model.run(return_timestamps=[1,2,3.1415,4,10])
        >>> model.run(return_timestamps=10)
        >>> model.run(return_timestamps=np.linspace(1,10,20))

        See Also
        --------
        pysd.set_components : handles setting model parameters
        pysd.set_initial_condition : handles setting initial conditions

        """

        if params:
            self.set_components(params)

        if initial_condition != 'current':
            self.set_initial_condition(initial_condition)

        tseries = self._build_timeseries(return_timestamps)

        # the odeint expects the first timestamp in the tseries to be the initial condition,
        # so we may need to add the t0 if it is not present in the tseries array
        addtflag = tseries[0] != self.components.t
        if addtflag:
            tseries = np.insert(tseries, 0, self.components.t)

        res = _odeint(func=self.components.d_dt,
                      y0=self.components.state_vector(),
                      t=tseries,
                      **intg_kwargs)
        #hmax=self.components.time_step())

        state_df = _pd.DataFrame(data=res,
                                 index=tseries,
                                 columns=self.components._stocknames)

        return_df = self.extend_dataframe(
            state_df, return_columns) if return_columns else state_df

        if addtflag:
            return_df.drop(return_df.index[0], inplace=True)

        if collect:
            self.record.append(
                return_df
            )  #we could just record the state, and expand it later...

        # The integrator takes us past the last point in the tseries.
        # Go back to it, in order to maintain the state at a predictable location.
        # This may take up more time than we're willing to spend...
        if self.components.t != tseries[-1]:
            self.set_state(tseries[-1], dict(state_df.iloc[-1]))

        return return_df