Source code for casiopeia.sim

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This file is part of casiopeia.
#
# Copyright 2014-2016 Adrian Bürger, Moritz Diehl
#
# casiopeia is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# casiopeia is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with casiopeia. If not, see <http://www.gnu.org/licenses/>.

'''The module ``casiopeia.sim`` contains the class used for system simulation.'''

import numpy as np

from interfaces import casadi_interface as ci
from intro import intro

import inputchecks

[docs]class Simulation(object): '''The class :class:`casiopeia.sim.Simulation` is used to simulate dynamic systems defined with the :class:`casiopeia.system.System` class. It is supposed that the system containsa number of time-constant parameters :math:`p`.''' @property def simulation_results(self): try: return self.__simulation_results except AttributeError: raise AttributeError(''' A system simulation has to be executed before the simulation results can be accessed, please run run_system_simulation() first. ''') def __generate_simulation_ode(self, pdata, qdata): p = inputchecks.check_parameter_data(pdata, self.__system.np) q = inputchecks.check_constant_controls_data(qdata, self.__system.nq) ode_fcn = ci.mx_function("ode_fcn", \ [self.__system.u, self.__system.q, self.__system.x, \ self.__system.eps_u, self.__system.p], \ [self.__system.f]) # Needs to be changes for allowance of explicit time dependecy! self.__ode_parameters_applied = ode_fcn.call([ \ self.__system.u, q, self.__system.x, \ np.zeros(self.__system.neps_u), p])[0] def __generate_scaled_dae(self): # ODE time scaling according to: # https://groups.google.com/forum/#!topic/casadi-users/AeXzJmBH0-Y t_scale = ci.mx_sym("t_scale", 1) self.__dae_scaled = {"x": self.__system.x, \ "p": ci.vertcat([t_scale, self.__system.u]), \ "ode": t_scale * self.__ode_parameters_applied} def __init__(self, system, pdata, qdata = None): r''' :param system: system considered for simulation, specified using the :class:`casiopeia.system.System` class :type system: casiopeia.system.System :param pdata: values of the time-constant parameters :math:`p \in \mathbb{R}^{\text{n}_\text{p}}` :type pdata: numpy.ndarray, casadi.DMatrix :param qdata: optional, values of the time-constant controls :math:`q \in \mathbb{R}^{\text{n}_\text{q}}`; if no values are given, 0 will be used :type qdata: numpy.ndarray, casadi.DMatrix ''' intro() self.__system = inputchecks.set_system(system) self.__generate_simulation_ode(pdata, qdata) self.__generate_scaled_dae() def __initialize_simulation(self, x0, time_points, udata, \ integrator_options_user): self.__x0 = inputchecks.check_states_data(x0, self.__system.nx, 0) time_points = inputchecks.check_time_points_input(time_points) number_of_integration_steps = time_points.size - 1 time_steps = time_points[1:] - time_points[:-1] udata = inputchecks.check_controls_data(udata, self.__system.nu, \ number_of_integration_steps) self.__simulation_input = ci.vertcat([np.atleast_2d(time_steps), udata]) integrator_options = integrator_options_user.copy() integrator_options.update({"t0": 0, "tf": 1, "expand": True}) # , "number_of_finite_elements": 1}) # integrator = ci.Integrator("integrator", "rk", \ integrator = ci.Integrator("integrator", "cvodes", \ self.__dae_scaled, integrator_options) self.__simulation = integrator.mapaccum("simulation", \ number_of_integration_steps)
[docs] def run_system_simulation(self, x0, time_points, udata = None, \ integrator_options = {}, print_status = True): r''' :param x0: state values :math:`x_0 \in \mathbb{R}^{\text{n}_\text{x}}` at the first time point :math:`t_0` :type x0: numpy.ndarray, casadi.DMatrix, list :param time_points: switching time points for the controls :math:`t_\text{N} \in \mathbb{R}^\text{N}` :type time_points: numpy.ndarray, casadi.DMatrix, list :param udata: optional, values for the time-varying controls at the first :math:`N-1` switching time points :math:`u_\text{N} \in \mathbb{R}^{\text{n}_\text{u} \times \text{N}-1}`; if no values are given, 0 will be used :type udata: numpy.ndarray, casadi.DMatrix :param integrator_options: optional, options to be passed to the CasADi integrator (see the CasADi documentation for a list of all possible options) :type integrator_options: dict :param print_status: optional, set to ``True`` (default) or ``False`` to enable or disable console printing. :type print_status: bool This function will run a system simulation for the specified initial state values and control data from :math:`t_0` to :math:`t_\text{N}`. If you receive integrator-related error messages during the simulation, please check the corresponding parts of the CasADi documentation. After the simulation has finished, the simulation results :math:`x_\text{N}` can be accessed via the class attribute ``Simulation.simulation_results``. ''' if print_status: print('\n' + '# ' + 23 * '-' + \ ' casiopeia system simulation ' + 22 * '-' + ' #') print('\nRunning system simulation, this might take some time ...') self.__initialize_simulation(x0 = x0, time_points = time_points, \ udata = udata, integrator_options_user = integrator_options) self.__simulation_results = ci.horzcat([ \ self.__x0, self.__simulation(x0 = self.__x0, p = self.__simulation_input)["xf"] ]) if print_status: print("\nSystem simulation finished.")