From b5f2b21a12010ba3c38c2d459db30c77758c90b1 Mon Sep 17 00:00:00 2001 From: Maximilian Schenke Date: Fri, 8 Oct 2021 15:29:15 +0200 Subject: [PATCH 01/79] added eesm to electric_motors --- .../externally_excited_synchronous_motor.py | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py diff --git a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py new file mode 100644 index 00000000..1cbe110b --- /dev/null +++ b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py @@ -0,0 +1,173 @@ +import math +import numpy as np + +from .synchronous_motor import SynchronousMotor + + +class ExternallyExcitedSynchronousMotor(SynchronousMotor): + """ + ===================== ========== ============= =========================================== + Motor Parameter Unit Default Value Description + ===================== ========== ============= =========================================== + r_s mOhm 15.55 Stator resistance + r_e mOhm 7.2 Excitation resistance + l_d mH 1.66 Direct axis inductance + l_q mH 0.35 Quadrature axis inductance + l_e mH 1.74 Excitation inductance + p 1 3 Pole pair number + j_rotor kg/m^2 0.3883 Moment of inertia of the rotor + ===================== ========== ============= =========================================== + + =============== ====== ============================================= + Motor Currents Unit Description + =============== ====== ============================================= + i_sd A Direct axis current + i_sq A Quadrature axis current + i_e A Excitation current + i_a A Current through branch a + i_b A Current through branch b + i_c A Current through branch c + i_alpha A Current in alpha axis + i_beta A Current in beta axis + =============== ====== ============================================= + =============== ====== ============================================= + Motor Voltages Unit Description + =============== ====== ============================================= + u_sd V Direct axis voltage + u_sq V Quadrature axis voltage + u_e V Exciting voltage + u_a V Voltage through branch a + u_b V Voltage through branch b + u_c V Voltage through branch c + u_alpha V Voltage in alpha axis + u_beta V Voltage in beta axis + =============== ====== ============================================= + + ======== =========================================================== + Limits / Nominal Value Dictionary Entries: + -------- ----------------------------------------------------------- + Entry Description + ======== =========================================================== + i General current limit / nominal value + i_a Current in phase a + i_b Current in phase b + i_c Current in phase c + i_alpha Current in alpha axis + i_beta Current in beta axis + i_sd Current in direct axis + i_sq Current in quadrature axis + i_e Current in excitation circuit + omega Mechanical angular Velocity + torque Motor generated torque + epsilon Electrical rotational angle + u_a Voltage in phase a + u_b Voltage in phase b + u_c Voltage in phase c + u_alpha Voltage in alpha axis + u_beta Voltage in beta axis + u_sd Voltage in direct axis + u_sq Voltage in quadrature axis + u_e Voltage in excitation circuit + ======== =========================================================== + + + Note: + The voltage limits should be the amplitude of the phase voltage (:math:`\hat{u}_S`). + Typically the rms value for the line voltage (:math:`U_L`) is given. + :math:`\hat{u}_S=\sqrt{2/3}~U_L` + + The current limits should be the amplitude of the phase current (:math:`\hat{i}_S`). + Typically the rms value for the phase current (:math:`I_S`) is given. + :math:`\hat{i}_S = \sqrt{2}~I_S` + + If not specified, nominal values are equal to their corresponding limit values. + Furthermore, if specific limits/nominal values (e.g. i_a) are not specified they are inferred from + the general limits/nominal values (e.g. i) + """ + + I_SD_IDX = 0 + I_SQ_IDX = 1 + I_E_IDX = 2 + EPSILON_IDX = 3 + CURRENTS_IDX = [0, 1, 2] + CURRENTS = ['i_sd', 'i_sq', 'i_e'] + VOLTAGES = ['u_sd', 'u_sq', 'u_e'] + + #### Parameters taken from DOI: 10.1109/ICELMACH.2014.6960287 (C. D. Nguyen; W. Hofmann) + _default_motor_parameter = { + 'p': 3, + 'l_d': 1.66e-3, + 'l_q': 0.35e-3, + 'l_e': 1.74e-3, + 'j_rotor': 0.3883, + 'r_s': 15.55e-3, + 'r_e': 7.2e-3, + } + HAS_JACOBIAN = True + _default_limits = dict(omega=12e3 * np.pi / 30, torque=0.0, i=150, i_e=150, epsilon=math.pi, u=320) + _default_nominal_values = dict(omega=4.3e3 * np.pi / 30, torque=0.0, i=120, i_e=150, epsilon=math.pi, u=320) + _default_initializer = { + 'states': {'i_sq': 0.0, 'i_sd': 0.0, 'i_e': 0.0, 'epsilon': 0.0}, + 'interval': None, + 'random_init': None, + 'random_params': (None, None) + } + + IO_VOLTAGES = ['u_a', 'u_b', 'u_c', 'u_sd', 'u_sq', 'u_e'] + IO_CURRENTS = ['i_a', 'i_b', 'i_c', 'i_sd', 'i_sq', 'i_e'] + + def _update_model(self): + # Docstring of superclass + mp = self._motor_parameter + self._model_constants = np.array([ + # omega, i_d, i_q, i_f, u_d, u_q, u_f omega * i_d, omega * i_q, omega * i_f + [ 0, -mp['r_s'], 0, 0, 1, 0, 0, 0, mp['l_q'] * mp['p'], 0], + [ 0, 0, -mp['r_s'], 0, 0, 1, 0, -mp['l_d'] * mp['p'], 0, -mp['p'] * mp['l_f']], + [ 0, 0, 0, -mp['r_e'], 0, 0, 1, 0, 0, 0], + [ mp['p'], 0, 0, 0, 0, 0, 0, 0, 0, 0], + ]) + + self._model_constants[self.I_SD_IDX] = self._model_constants[self.I_SD_IDX] / mp['l_d'] + self._model_constants[self.I_SQ_IDX] = self._model_constants[self.I_SQ_IDX] / mp['l_q'] + self._model_constants[self.I_E_IDX] = self._model_constants[self.I_E_IDX] / mp['l_e'] + + def _torque_limit(self): + # Docstring of superclass + mp = self._motor_parameter + if mp['l_d'] == mp['l_q']: + return self.torque([0, self._limits['i_sq'], self._limits['i_e'], 0]) + else: + i_n = self.nominal_values['i'] + _p = mp['psi_p'] / (2 * (mp['l_d'] - mp['l_q'])) + _q = - i_n ** 2 / 2 + i_d_opt = - _p / 2 - np.sqrt( (_p / 2) ** 2 - _q) + i_q_opt = np.sqrt(i_n ** 2 - i_d_opt ** 2) + return self.torque([i_d_opt, i_q_opt, self._limits['i_e'], 0]) + + def torque(self, currents): + # Docstring of superclass + mp = self._motor_parameter + return 1.5 * mp['p'] * (mp['l_e'] * currents[self.I_E_IDX] + (mp['l_d'] - mp['l_q']) * currents[self.I_SD_IDX]) * currents[self.I_SQ_IDX] + + def electrical_jacobian(self, state, u_in, omega, *args): + mp = self._motor_parameter + return ( + np.array([ # dx'/dx + [ -mp['r_s'] / mp['l_d'], mp['l_q'] / mp['l_d'] * omega * mp['p'], 0, 0], + [-mp['l_d'] / mp['l_q'] * omega * mp['p'], - mp['r_s'] / mp['l_q'], -omega * mp['p'] * mp['l_e'] / mp['l_q'], 0], + [ 0, 0, -mp['r_e'] / mp['l_e'], 0], + [ 0, 0, 0, 0], + ]), + np.array([ # dx'/dw + mp['p'] * mp['l_q'] / mp['l_d'] * state[self.I_SQ_IDX], + - mp['p'] * mp['l_d'] / mp['l_q'] * state[self.I_SD_IDX] - mp['p'] * mp['psi_p'] / mp['l_q'], + 0, + mp['p'], + ]), + np.array([ # dT/dx + 1.5 * mp['p'] * (mp['l_d'] - mp['l_q']) * state[self.I_SQ_IDX], + 1.5 * mp['p'] * (mp['l_e'] * state[self.I_E_IDX] + (mp['l_d'] - mp['l_q']) * state[self.I_SD_IDX]), + 1.5 * mp['p'] * mp['l_e'] * state[self.I_SQ_IDX], + 0, + ]) + ) From 853d5c756c1084ba152e402005a03c46e5de6344 Mon Sep 17 00:00:00 2001 From: Maximilian Schenke Date: Mon, 11 Oct 2021 16:45:58 +0200 Subject: [PATCH 02/79] added retroaction between direct and excitation circuit --- .../externally_excited_synchronous_motor.py | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py index 1cbe110b..bd3be288 100644 --- a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py @@ -98,7 +98,8 @@ class ExternallyExcitedSynchronousMotor(SynchronousMotor): 'p': 3, 'l_d': 1.66e-3, 'l_q': 0.35e-3, - 'l_e': 1.74e-3, + 'l_m': 000, + 'l_e': 000, 'j_rotor': 0.3883, 'r_s': 15.55e-3, 'r_e': 7.2e-3, @@ -119,12 +120,13 @@ class ExternallyExcitedSynchronousMotor(SynchronousMotor): def _update_model(self): # Docstring of superclass mp = self._motor_parameter + sigma = 1 - mp['l_m'] ** 2 / (mp['l_d'] * mp['l_e']) self._model_constants = np.array([ - # omega, i_d, i_q, i_f, u_d, u_q, u_f omega * i_d, omega * i_q, omega * i_f - [ 0, -mp['r_s'], 0, 0, 1, 0, 0, 0, mp['l_q'] * mp['p'], 0], - [ 0, 0, -mp['r_s'], 0, 0, 1, 0, -mp['l_d'] * mp['p'], 0, -mp['p'] * mp['l_f']], - [ 0, 0, 0, -mp['r_e'], 0, 0, 1, 0, 0, 0], - [ mp['p'], 0, 0, 0, 0, 0, 0, 0, 0, 0], + # omega, i_d, i_q, i_e, u_d, u_q, u_e, omega * i_d, omega * i_q, omega * i_e + [ 0, -mp['r_s'] / sigma, 0, mp['l_m'] * mp['r_e'] / (sigma * mp['l_e']), 1 / sigma, 0, -mp['l_m'] / (sigma * mp['l_e']), 0, mp['l_q'] * mp['p'] / sigma, 0], + [ 0, 0, -mp['r_s'], 0, 0, 1, 0, -mp['l_d'] * mp['p'], 0, -mp['p'] * mp['l_m']], + [ 0, mp['l_m'] * mp['r_s'] / (sigma * mp['l_d']), 0, -mp['r_e'] / sigma, -mp['l_m'] / (sigma * mp['l_d']), 0, 1 / sigma, 0, -mp['p'] * mp['l_m'] * mp['l_q'] / (sigma * mp['l_d']), 0], + [ mp['p'], 0, 0, 0, 0, 0, 0, 0, 0, 0], ]) self._model_constants[self.I_SD_IDX] = self._model_constants[self.I_SD_IDX] / mp['l_d'] @@ -134,6 +136,7 @@ def _update_model(self): def _torque_limit(self): # Docstring of superclass mp = self._motor_parameter + # I am not sure about this if mp['l_d'] == mp['l_q']: return self.torque([0, self._limits['i_sq'], self._limits['i_e'], 0]) else: @@ -147,21 +150,22 @@ def _torque_limit(self): def torque(self, currents): # Docstring of superclass mp = self._motor_parameter - return 1.5 * mp['p'] * (mp['l_e'] * currents[self.I_E_IDX] + (mp['l_d'] - mp['l_q']) * currents[self.I_SD_IDX]) * currents[self.I_SQ_IDX] + return 1.5 * mp['p'] * (mp['l_m'] * currents[self.I_E_IDX] + (mp['l_d'] - mp['l_q']) * currents[self.I_SD_IDX]) * currents[self.I_SQ_IDX] def electrical_jacobian(self, state, u_in, omega, *args): mp = self._motor_parameter + sigma = 1 - mp['l_m'] ** 2 / (mp['l_d'] * mp['l_e']) return ( np.array([ # dx'/dx - [ -mp['r_s'] / mp['l_d'], mp['l_q'] / mp['l_d'] * omega * mp['p'], 0, 0], - [-mp['l_d'] / mp['l_q'] * omega * mp['p'], - mp['r_s'] / mp['l_q'], -omega * mp['p'] * mp['l_e'] / mp['l_q'], 0], - [ 0, 0, -mp['r_e'] / mp['l_e'], 0], - [ 0, 0, 0, 0], + [ -mp['r_s'] / mp['l_d'], mp['l_q'] / (sigma * mp['l_d']) * omega * mp['p'], mp['l_m'] * mp['r_e'] / (sigma * mp['l_d'] * mp['l_e']), 0], + [ -mp['l_d'] / mp['l_q'] * omega * mp['p'], -mp['r_s'] / mp['l_q'], -omega * mp['p'] * mp['l_e'] / mp['l_q'], 0], + [mp['l_m'] * mp['r_s'] / (sigma * mp['l_d'] * mp['l_e']), -omega * mp['p'] * mp['l_m'] * mp['l_q'] / (sigma * mp['l_d'] * mp['l_e']), -mp['r_e'] / mp['l_e'], 0], + [ 0, 0, 0, 0], ]), np.array([ # dx'/dw mp['p'] * mp['l_q'] / mp['l_d'] * state[self.I_SQ_IDX], - - mp['p'] * mp['l_d'] / mp['l_q'] * state[self.I_SD_IDX] - mp['p'] * mp['psi_p'] / mp['l_q'], - 0, + -mp['p'] * mp['l_d'] / mp['l_q'] * state[self.I_SD_IDX] - mp['p'] * mp['l_m'] / mp['l_q'] * state[self.I_E_IDX], + -mp['p'], mp['p'], ]), np.array([ # dT/dx From ad3106cfbd8e1a52ff440d5174c86994127c4057 Mon Sep 17 00:00:00 2001 From: Arne Date: Sun, 14 Nov 2021 21:34:04 +0100 Subject: [PATCH 03/79] First version of the state action processors --- .../classic_controllers.py | 29 ++++--- .../classic_controllers_dc_motor_example.py | 10 +-- gym_electric_motor/core.py | 10 ++- .../cont_cc_shunt_dc_env.py | 5 +- .../state_action_processors/__init__.py | 2 + .../current_sum_processor.py | 37 +++++++++ .../state_action_processor.py | 79 +++++++++++++++++++ 7 files changed, 152 insertions(+), 20 deletions(-) create mode 100644 gym_electric_motor/state_action_processors/__init__.py create mode 100644 gym_electric_motor/state_action_processors/current_sum_processor.py create mode 100644 gym_electric_motor/state_action_processors/state_action_processor.py diff --git a/examples/classic_controllers/classic_controllers.py b/examples/classic_controllers/classic_controllers.py index 2ae0cdb8..065d72bd 100644 --- a/examples/classic_controllers/classic_controllers.py +++ b/examples/classic_controllers/classic_controllers.py @@ -133,7 +133,7 @@ def reference_states(environment, **controller_kwargs): def find_controller_type(environment, stages, **controller_kwargs): _stages = stages - if isinstance(environment.physical_system, DcMotorSystem): + if isinstance(environment.physical_system.unwrapped, DcMotorSystem): if type(stages) is list: if len(stages) > 1: if type(stages[0]) is list: @@ -151,7 +151,7 @@ def find_controller_type(environment, stages, **controller_kwargs): else: controller_type = stages _stages = [{'controller_type': stages}] - elif isinstance(environment.physical_system, SynchronousMotorSystem): + elif isinstance(environment.physical_system.unwrapped, SynchronousMotorSystem): if len(stages) == 2: if len(stages[1]) == 1 and 'i_sq' in controller_kwargs['ref_states']: controller_type = 'foc_controller' @@ -169,7 +169,7 @@ def automated_controller_design(environment, **controller_kwargs): action_space_type = type(environment.action_space) ref_states = controller_kwargs['ref_states'] stages = [] - if isinstance(environment.physical_system, DcMotorSystem): # Checking type of motor + if isinstance(environment.physical_system.unwrapped, DcMotorSystem): # Checking type of motor if 'omega' in ref_states or 'torque' in ref_states: # Checking control task controller_type = 'cascaded_controller' @@ -198,7 +198,7 @@ def automated_controller_design(environment, **controller_kwargs): else: stages = [stages, [{'controller_type': 'three_point'}]] - elif isinstance(environment.physical_system, SynchronousMotorSystem): + elif isinstance(environment.physical_system.unwrapped, SynchronousMotorSystem): if 'i_sq' in ref_states or 'torque' in ref_states: # Checking control task controller_type = 'foc_controller' if 'i_sq' in ref_states else 'cascaded_foc_controller' if action_space_type is Discrete: @@ -231,13 +231,13 @@ def automated_gain(environment, stages, controller_type, **controller_kwargs): mp = environment.physical_system.electrical_motor.motor_parameter limits = environment.physical_system.limits omega_lim = limits[environment.state_names.index('omega')] - if isinstance(environment.physical_system, DcMotorSystem): + if isinstance(environment.physical_system.unwrapped, DcMotorSystem): i_a_lim = limits[environment.physical_system.CURRENTS_IDX[0]] i_e_lim = limits[environment.physical_system.CURRENTS_IDX[-1]] u_a_lim = limits[environment.physical_system.VOLTAGES_IDX[0]] u_e_lim = limits[environment.physical_system.VOLTAGES_IDX[-1]] - elif isinstance(environment.physical_system, SynchronousMotorSystem): + elif isinstance(environment.physical_system.unwrapped, SynchronousMotorSystem): i_sd_lim = limits[environment.state_names.index('i_sd')] i_sq_lim = limits[environment.state_names.index('i_sq')] u_sd_lim = limits[environment.state_names.index('u_sd')] @@ -250,7 +250,7 @@ def automated_gain(environment, stages, controller_type, **controller_kwargs): if isinstance(environment.physical_system.electrical_motor, DcSeriesMotor): mp['l'] = mp['l_a'] + mp['l_e'] - elif isinstance(environment.physical_system, DcMotorSystem): + elif isinstance(environment.physical_system.unwrapped, DcMotorSystem): mp['l'] = mp['l_a'] if 'automated_gain' not in controller_kwargs.keys() or automated_gain: @@ -432,8 +432,9 @@ class ContinuousActionController(Controller): """ def __init__(self, environment, stages, ref_states, external_ref_plots=[], **controller_kwargs): - assert type(environment.action_space) is Box and isinstance(environment.physical_system, - DcMotorSystem), 'No suitable action space for Continuous Action Controller' + assert type(environment.action_space) is Box\ + and isinstance(environment.physical_system.unwrapped, DcMotorSystem), \ + 'No suitable action space for Continuous Action Controller' self.action_space = environment.action_space self.state_names = environment.state_names self.ref_idx = np.where(ref_states != 'i_e')[0][0] @@ -510,9 +511,10 @@ class DiscreteActionController(Controller): controller is used. For the externally excited dc motor, the excitation current is also controlled. """ - def __init__(self, environment, stages, ref_states, external_ref_plots=[], **controller_kwargs): - assert type(environment.action_space) in [Discrete, MultiDiscrete] and isinstance(environment.physical_system, - DcMotorSystem), 'No suitable action space for Discrete Action Controller' + def __init__(self, environment, stages, ref_states, external_ref_plots=(), **controller_kwargs): + assert type(environment.action_space) in [Discrete, MultiDiscrete] \ + and isinstance(environment.physical_system.unwrapped, DcMotorSystem),\ + 'No suitable action space for Discrete Action Controller' self.ref_idx = np.where(ref_states != 'i_e')[0][0] self.ref_state_idx = environment.state_names.index(ref_states[self.ref_idx]) self.i_idx = environment.physical_system.CURRENTS_IDX[-1] @@ -706,7 +708,8 @@ class FieldOrientedController(Controller): """ def __init__(self, environment, stages, ref_states, external_ref_plots=[], **controller_kwargs): - assert isinstance(environment.physical_system, SynchronousMotorSystem), 'No suitable Environment for FOC Controller' + assert isinstance(environment.physical_system.unwrapped, SynchronousMotorSystem),\ + 'No suitable Environment for FOC Controller' t32 = environment.physical_system.electrical_motor.t_32 q = environment.physical_system.electrical_motor.q diff --git a/examples/classic_controllers/classic_controllers_dc_motor_example.py b/examples/classic_controllers/classic_controllers_dc_motor_example.py index 51190e10..6d613be2 100644 --- a/examples/classic_controllers/classic_controllers_dc_motor_example.py +++ b/examples/classic_controllers/classic_controllers_dc_motor_example.py @@ -19,8 +19,8 @@ 'Finite' Discrete Action Space """ - motor_type = 'PermExDc' - control_type = 'SC' + motor_type = 'ShuntDc' + control_type = 'CC' action_type = 'Cont' motor = action_type + '-' + control_type + '-' + motor_type + '-v0' @@ -35,10 +35,10 @@ raise KeyError(motor_type + ' is not available') # definition of the plotted variables - external_ref_plots = [ExternallyReferencedStatePlot(state) for state in states] + #external_ref_plots = [ExternallyReferencedStatePlot(state) for state in states] # initialize the gym-electric-motor environment - env = gem.make(motor, visualization=MotorDashboard(additional_plots=external_ref_plots)) + env = gem.make(motor, visualization=MotorDashboard(state_plots='all')) """ initialize the controller @@ -51,7 +51,7 @@ a (optional) tuning parameter of the symmetrical optimum (default: 4) """ - controller = Controller.make(env, external_ref_plots=external_ref_plots) + controller = Controller.make(env) state, reference = env.reset() diff --git a/gym_electric_motor/core.py b/gym_electric_motor/core.py index 30389ae4..14902be3 100644 --- a/gym_electric_motor/core.py +++ b/gym_electric_motor/core.py @@ -165,7 +165,7 @@ def visualizations(self): return self._visualizations def __init__(self, physical_system, reference_generator, reward_function, visualization=(), state_filter=None, - callbacks=(), constraints=(), **kwargs): + callbacks=(), constraints=(), state_action_processors=(), **kwargs): """ Setting and initialization of all environments' modules. @@ -184,6 +184,8 @@ def __init__(self, physical_system, reference_generator, reward_function, visual ConstraintMonitor in the environment. visualization(iterable(ElectricMotorVisualization)/None): The visualizations of this environment. state_filter(list(str)): Selection of states that are shown in the observation. + state_action_processors(iterable(StateActionProcessor)): StateActionProcessor instances to be wrapped around + the physical system. callbacks(list(Callback)): Callbacks being called in the environment **kwargs: Arguments to be passed to the modules. """ @@ -205,6 +207,8 @@ def __init__(self, physical_system, reference_generator, reward_function, visual self._constraint_monitor = cm # Announcement of the modules among each other + for state_action_processor in state_action_processors: + self._physical_system = state_action_processor.set_physical_system(self._physical_system) self._reference_generator.set_modules(self.physical_system) self._constraint_monitor.set_modules(self.physical_system) self._reward_function.set_modules(self.physical_system, self._reference_generator, self._constraint_monitor) @@ -498,6 +502,10 @@ class PhysicalSystem: """The Physical System module encapsulates the physical model of the system as well as the simulation from one step to the next.""" + @property + def unwrapped(self): + return self + @property def k(self): """ diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py index fe32fc09..3a94dc85 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py @@ -6,6 +6,7 @@ from gym_electric_motor import physical_systems as ps from gym_electric_motor.reward_functions import WeightedSumOfErrors from gym_electric_motor.utils import initialize +from gym_electric_motor.state_action_processors import CurrentSumProcessor class ContCurrentControlDcShuntMotorEnv(ElectricMotorEnvironment): @@ -134,6 +135,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve calc_jacobian=calc_jacobian, tau=tau ) + reference_generator = initialize( ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, dict(reference_state='i_a') ) @@ -145,5 +147,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization, MotorDashboard, dict(state_plots=('i_a',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=(CurrentSumProcessor(('i_a', 'i_e')),) ) diff --git a/gym_electric_motor/state_action_processors/__init__.py b/gym_electric_motor/state_action_processors/__init__.py new file mode 100644 index 00000000..ecf6d737 --- /dev/null +++ b/gym_electric_motor/state_action_processors/__init__.py @@ -0,0 +1,2 @@ +from .state_action_processor import StateActionProcessor +from .current_sum_processor import CurrentSumProcessor \ No newline at end of file diff --git a/gym_electric_motor/state_action_processors/current_sum_processor.py b/gym_electric_motor/state_action_processors/current_sum_processor.py new file mode 100644 index 00000000..3e38334a --- /dev/null +++ b/gym_electric_motor/state_action_processors/current_sum_processor.py @@ -0,0 +1,37 @@ +import gym +import numpy as np + +from gym_electric_motor.state_action_processors import StateActionProcessor + + +class CurrentSumProcessor(StateActionProcessor): + + def __init__(self, currents): + super(CurrentSumProcessor, self).__init__() + self._currents = currents + self._current_indices = [] + + def set_physical_system(self, physical_system): + super().set_physical_system(physical_system) + low = np.concatenate((physical_system.state_space.low, [-1.])) + high = np.concatenate((physical_system.state_space.high, [1.])) + self.state_space = gym.spaces.Box(low, high, dtype=np.float64) + self._current_indices = [physical_system.state_positions[current] for current in self._currents] + current_limit = max(physical_system.limits[self._current_indices]) + current_nominal_value = max(physical_system.nominal_state[self._current_indices]) + self._limits = np.concatenate((physical_system.limits, [current_limit])) + self._nominal_state = np.concatenate((physical_system.nominal_state, [current_nominal_value])) + self._state_names = physical_system.state_names + ['i_sum'] + self._state_positions = {key: index for index, key in enumerate(self._state_names)} + return self + + def reset(self): + state = self._physical_system.reset() + return np.concatenate((state, [self._get_current_sum(state)])) + + def simulate(self, action): + state = self._physical_system.simulate(action) + return np.concatenate((state, [self._get_current_sum(state)])) + + def _get_current_sum(self, state): + return np.sum(state[self._current_indices]) diff --git a/gym_electric_motor/state_action_processors/state_action_processor.py b/gym_electric_motor/state_action_processors/state_action_processor.py new file mode 100644 index 00000000..64137047 --- /dev/null +++ b/gym_electric_motor/state_action_processors/state_action_processor.py @@ -0,0 +1,79 @@ +import gym_electric_motor as gem + + +class StateActionProcessor(gem.core.PhysicalSystem): + + @property + def action_space(self): + if self._action_space is None: + return self._physical_system.action_space + return self._action_space + + @action_space.setter + def action_space(self, space): + self._action_space = space + + @property + def state_space(self): + if self._state_space is None: + return self._physical_system.state_space + return self._state_space + + @state_space.setter + def state_space(self, space): + self._state_space = space + + @property + def physical_system(self): + return self._physical_system + + @property + def limits(self): + if self._limits is None: + return self._physical_system.limits + return self._limits + + @property + def unwrapped(self): + return self.physical_system.unwrapped + + @property + def nominal_state(self): + if self._nominal_state is None: + return self._physical_system.nominal_state + return self._nominal_state + + @property + def state_names(self): + if self._state_names is None: + return self._physical_system.state_names + return self._state_names + + def __init__(self): + super().__init__(None, None, (), None) + self._physical_system = None + self._limits = None + self._nominal_state = None + + def set_physical_system(self, physical_system): + self._physical_system = physical_system + self._action_space = physical_system.action_space + self._state_names = physical_system.state_names + self._state_positions = {key: index for index, key in enumerate(self._state_names)} + self._tau = physical_system.tau + return self + + def __getattr__(self, name): + return getattr(self._physical_system, name) + + def simulate(self, action): + return self._physical_system.simulate(action) + + def reset(self, **kwargs): + return self._physical_system.reset(**kwargs) + + def __str__(self): + return "<{}{}>".format(type(self).__name__, self.env) + + def __repr__(self): + return str(self) From 88ccd7e7bcd85ca4c71d626e0e44d1a2c6edd145 Mon Sep 17 00:00:00 2001 From: Arne Date: Sat, 20 Nov 2021 16:01:49 +0100 Subject: [PATCH 04/79] First version of the state action processors --- docs/index.rst | 1 + .../current_sum_processor.rst | 6 +++ .../state_action_processor.rst | 14 +++++++ .../abc_to_dq_action_processor.py | 36 +++++++++++++++++ .../current_sum_processor.py | 10 +++-- .../state_action_processors/flux_observer.py | 39 +++++++++++++++++++ .../state_action_processor.py | 27 +++++++++++-- .../state_noise_processor.py | 39 +++++++++++++++++++ 8 files changed, 164 insertions(+), 8 deletions(-) create mode 100644 docs/parts/state_action_processors/current_sum_processor.rst create mode 100644 docs/parts/state_action_processors/state_action_processor.rst create mode 100644 gym_electric_motor/state_action_processors/abc_to_dq_action_processor.py create mode 100644 gym_electric_motor/state_action_processors/flux_observer.py create mode 100644 gym_electric_motor/state_action_processors/state_noise_processor.py diff --git a/docs/index.rst b/docs/index.rst index f7e851d9..3bf427eb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -43,6 +43,7 @@ documentation specifies the basic interfaces inside a physical system. parts/reference_generators/reference_generator parts/reward_functions/reward_function parts/physical_systems/physical_system + parts/state_action_processors/state_action_processor parts/visualizations/visualization parts/constraint_monitor parts/core diff --git a/docs/parts/state_action_processors/current_sum_processor.rst b/docs/parts/state_action_processors/current_sum_processor.rst new file mode 100644 index 00000000..9ffad1d6 --- /dev/null +++ b/docs/parts/state_action_processors/current_sum_processor.rst @@ -0,0 +1,6 @@ +Current Sum Processor +##################### + +.. autoclass:: gym_electric_motor.state_action_processors.current_sum_processor.CurrentSumProcessor + :members: + :inherited-members: \ No newline at end of file diff --git a/docs/parts/state_action_processors/state_action_processor.rst b/docs/parts/state_action_processors/state_action_processor.rst new file mode 100644 index 00000000..32fa698f --- /dev/null +++ b/docs/parts/state_action_processors/state_action_processor.rst @@ -0,0 +1,14 @@ +State Action Processor +####################### + +.. toctree:: + :maxdepth: 1 + :caption: Available StateActionProcessors: + + current_sum_processor + +State Action Processor Base Class +'''''''''''''''''''''''''''''''''' + +.. autoclass:: gym_electric_motor.state_action_processors.StateActionProcessor + :members: \ No newline at end of file diff --git a/gym_electric_motor/state_action_processors/abc_to_dq_action_processor.py b/gym_electric_motor/state_action_processors/abc_to_dq_action_processor.py new file mode 100644 index 00000000..146e105f --- /dev/null +++ b/gym_electric_motor/state_action_processors/abc_to_dq_action_processor.py @@ -0,0 +1,36 @@ +import gym +import numpy as np + +from gym_electric_motor.physical_systems.physical_systems import SCMLSystem +from gym_electric_motor.physical_systems.electric_motors import ThreePhaseMotor +from gym_electric_motor.state_action_processors import StateActionProcessor + + +class AbcToDqActionProcessor(StateActionProcessor): + + @property + def action_space(self): + return gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) + + def __init__(self, physical_system=None): + self._angle = 0.0 + self._angle_index = None + super().__init__(physical_system) + + def set_physical_system(self, physical_system): + assert isinstance(physical_system.unwrapped, SCMLSystem) + assert isinstance(physical_system.electrical_motor, ThreePhaseMotor) + super().set_physical_system(physical_system) + self._angle_index = physical_system.state_names.index('epsilon') + return self + + def reset(self): + state = self._physical_system.reset() + return np.concatenate((state, [self._get_current_sum(state)])) + + def simulate(self, action): + state = self._physical_system.simulate(action) + return np.concatenate((state, [self._get_current_sum(state)])) + + def _get_epsilon(self, state): + return state[self._epsilon_idx] diff --git a/gym_electric_motor/state_action_processors/current_sum_processor.py b/gym_electric_motor/state_action_processors/current_sum_processor.py index 3e38334a..401bd86f 100644 --- a/gym_electric_motor/state_action_processors/current_sum_processor.py +++ b/gym_electric_motor/state_action_processors/current_sum_processor.py @@ -6,10 +6,12 @@ class CurrentSumProcessor(StateActionProcessor): - def __init__(self, currents): - super(CurrentSumProcessor, self).__init__() + def __init__(self, currents, limit='max', physical_system=None): self._currents = currents + assert limit in ['max', 'sum'] + self._limit = max if limit == 'max' else np.sum self._current_indices = [] + super().__init__(physical_system) def set_physical_system(self, physical_system): super().set_physical_system(physical_system) @@ -17,8 +19,8 @@ def set_physical_system(self, physical_system): high = np.concatenate((physical_system.state_space.high, [1.])) self.state_space = gym.spaces.Box(low, high, dtype=np.float64) self._current_indices = [physical_system.state_positions[current] for current in self._currents] - current_limit = max(physical_system.limits[self._current_indices]) - current_nominal_value = max(physical_system.nominal_state[self._current_indices]) + current_limit = self._limit(physical_system.limits[self._current_indices]) + current_nominal_value = self._limit(physical_system.nominal_state[self._current_indices]) self._limits = np.concatenate((physical_system.limits, [current_limit])) self._nominal_state = np.concatenate((physical_system.nominal_state, [current_nominal_value])) self._state_names = physical_system.state_names + ['i_sum'] diff --git a/gym_electric_motor/state_action_processors/flux_observer.py b/gym_electric_motor/state_action_processors/flux_observer.py new file mode 100644 index 00000000..57f72b2a --- /dev/null +++ b/gym_electric_motor/state_action_processors/flux_observer.py @@ -0,0 +1,39 @@ +import gym +import numpy as np + +import gym_electric_motor as gem +from gym_electric_motor.state_action_processors import StateActionProcessor + + +class FluxObserver(StateActionProcessor): + + def __init__(self, current_names=('i_sd', 'i_sq'), physical_system=None): + super(FluxObserver, self).__init__(physical_system) + self._current_indices = None + self._current_names = current_names + + def set_physical_system(self, physical_system): + assert isinstance(physical_system.electrical_motor, gem.physical_systems.electric_motors.InductionMotor) + super().set_physical_system(physical_system) + low = np.concatenate((physical_system.state_space.low, [-1., -1.])) + high = np.concatenate((physical_system.state_space.high, [1., 1.])) + self.state_space = gym.spaces.Box(low, high, dtype=np.float64) + self._current_indices = [physical_system.state_positions[name] for name in self._current_names] + current_limit = self._limit(physical_system.limits[self._current_indices]) + current_nominal_value = self._limit(physical_system.nominal_state[self._current_indices]) + self._limits = np.concatenate((physical_system.limits, [current_limit])) + self._nominal_state = np.concatenate((physical_system.nominal_state, [current_nominal_value])) + self._state_names = physical_system.state_names + ['i_sum'] + self._state_positions = {key: index for index, key in enumerate(self._state_names)} + return self + + def reset(self): + state = self._physical_system.reset() + return np.concatenate((state, [self._get_current_sum(state)])) + + def simulate(self, action): + state = self._physical_system.simulate(action) + return np.concatenate((state, [self._get_current_sum(state)])) + + def _get_current_sum(self, state): + return np.sum(state[self._current_indices]) diff --git a/gym_electric_motor/state_action_processors/state_action_processor.py b/gym_electric_motor/state_action_processors/state_action_processor.py index 64137047..6279eaff 100644 --- a/gym_electric_motor/state_action_processors/state_action_processor.py +++ b/gym_electric_motor/state_action_processors/state_action_processor.py @@ -1,10 +1,19 @@ import gym_electric_motor as gem -class StateActionProcessor(gem.core.PhysicalSystem): +class StateActionProcessor(gem.core.PhysicalSystem, gem.core.RandomComponent): + """A StateActionProcessor is a wrapper around the PhysicalSystem of an gem-environment. + + It may be used to modify its states and actions. In contrast to gym-wrappers which are put around the whole + whole environment, modified states by the StateActionProcessors can be referenced, rewarded and visualized by the + other components of the environment. + """ @property def action_space(self): + """The processed action space. + + If it is unset, the action space of the inner physical system is returned.""" if self._action_space is None: return self._physical_system.action_space return self._action_space @@ -15,6 +24,9 @@ def action_space(self, space): @property def state_space(self): + """The processed state space. + + If it is unset, the state space of the inner physical system is returned.""" if self._state_space is None: return self._physical_system.state_space return self._state_space @@ -25,6 +37,7 @@ def state_space(self, space): @property def physical_system(self): + """The inner physical_system""" return self._physical_system @property @@ -49,11 +62,13 @@ def state_names(self): return self._physical_system.state_names return self._state_names - def __init__(self): + def __init__(self, physical_system=None): super().__init__(None, None, (), None) - self._physical_system = None + self._physical_system = physical_system self._limits = None self._nominal_state = None + if physical_system is not None: + self.set_physical_system(physical_system) def set_physical_system(self, physical_system): self._physical_system = physical_system @@ -63,6 +78,10 @@ def set_physical_system(self, physical_system): self._tau = physical_system.tau return self + def seed(self, seed=None): + if isinstance(self._physical_system, gem.core.RandomComponent): + self._physical_system.seed(seed) + def __getattr__(self, name): return getattr(self._physical_system, name) @@ -73,7 +92,7 @@ def reset(self, **kwargs): return self._physical_system.reset(**kwargs) def __str__(self): - return "<{}{}>".format(type(self).__name__, self.env) + return "<{}{}>".format(type(self).__name__, self.physical_system) def __repr__(self): return str(self) diff --git a/gym_electric_motor/state_action_processors/state_noise_processor.py b/gym_electric_motor/state_action_processors/state_noise_processor.py new file mode 100644 index 00000000..401bd86f --- /dev/null +++ b/gym_electric_motor/state_action_processors/state_noise_processor.py @@ -0,0 +1,39 @@ +import gym +import numpy as np + +from gym_electric_motor.state_action_processors import StateActionProcessor + + +class CurrentSumProcessor(StateActionProcessor): + + def __init__(self, currents, limit='max', physical_system=None): + self._currents = currents + assert limit in ['max', 'sum'] + self._limit = max if limit == 'max' else np.sum + self._current_indices = [] + super().__init__(physical_system) + + def set_physical_system(self, physical_system): + super().set_physical_system(physical_system) + low = np.concatenate((physical_system.state_space.low, [-1.])) + high = np.concatenate((physical_system.state_space.high, [1.])) + self.state_space = gym.spaces.Box(low, high, dtype=np.float64) + self._current_indices = [physical_system.state_positions[current] for current in self._currents] + current_limit = self._limit(physical_system.limits[self._current_indices]) + current_nominal_value = self._limit(physical_system.nominal_state[self._current_indices]) + self._limits = np.concatenate((physical_system.limits, [current_limit])) + self._nominal_state = np.concatenate((physical_system.nominal_state, [current_nominal_value])) + self._state_names = physical_system.state_names + ['i_sum'] + self._state_positions = {key: index for index, key in enumerate(self._state_names)} + return self + + def reset(self): + state = self._physical_system.reset() + return np.concatenate((state, [self._get_current_sum(state)])) + + def simulate(self, action): + state = self._physical_system.simulate(action) + return np.concatenate((state, [self._get_current_sum(state)])) + + def _get_current_sum(self, state): + return np.sum(state[self._current_indices]) From c5ffed55193bfa06dd8faab5b6282d8fa8d8839e Mon Sep 17 00:00:00 2001 From: Arne Date: Sun, 21 Nov 2021 22:17:06 +0100 Subject: [PATCH 05/79] First version of the state action processors --- .../physical_systems/__init__.py | 5 +- .../abc_to_dq_action_processor.py | 36 ------------ .../dq_to_abc_action_processor.py | 58 +++++++++++++++++++ .../state_action_processors/flux_observer.py | 46 +++++++++++---- 4 files changed, 96 insertions(+), 49 deletions(-) delete mode 100644 gym_electric_motor/state_action_processors/abc_to_dq_action_processor.py create mode 100644 gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py diff --git a/gym_electric_motor/physical_systems/__init__.py b/gym_electric_motor/physical_systems/__init__.py index 486fa042..7861559a 100644 --- a/gym_electric_motor/physical_systems/__init__.py +++ b/gym_electric_motor/physical_systems/__init__.py @@ -1,11 +1,12 @@ -from .physical_systems import DcMotorSystem, SynchronousMotorSystem, SquirrelCageInductionMotorSystem, DoublyFedInductionMotorSystem +from .physical_systems import DcMotorSystem, SynchronousMotorSystem, SquirrelCageInductionMotorSystem, \ + DoublyFedInductionMotorSystem, ThreePhaseMotorSystem, SCMLSystem from .converters import PowerElectronicConverter, FiniteOneQuadrantConverter, FiniteTwoQuadrantConverter, \ FiniteFourQuadrantConverter, FiniteMultiConverter, FiniteB6BridgeConverter, ContOneQuadrantConverter, \ ContTwoQuadrantConverter, ContFourQuadrantConverter, ContMultiConverter, ContB6BridgeConverter, NoConverter from .electric_motors import DcExternallyExcitedMotor, DcSeriesMotor, DcPermanentlyExcitedMotor, DcShuntMotor, \ PermanentMagnetSynchronousMotor, ElectricMotor, SynchronousReluctanceMotor, SquirrelCageInductionMotor, \ - DoublyFedInductionMotor + DoublyFedInductionMotor, ThreePhaseMotor from .mechanical_loads import MechanicalLoad, PolynomialStaticLoad, ExternalSpeedLoad, ConstantSpeedLoad, \ OrnsteinUhlenbeckLoad diff --git a/gym_electric_motor/state_action_processors/abc_to_dq_action_processor.py b/gym_electric_motor/state_action_processors/abc_to_dq_action_processor.py deleted file mode 100644 index 146e105f..00000000 --- a/gym_electric_motor/state_action_processors/abc_to_dq_action_processor.py +++ /dev/null @@ -1,36 +0,0 @@ -import gym -import numpy as np - -from gym_electric_motor.physical_systems.physical_systems import SCMLSystem -from gym_electric_motor.physical_systems.electric_motors import ThreePhaseMotor -from gym_electric_motor.state_action_processors import StateActionProcessor - - -class AbcToDqActionProcessor(StateActionProcessor): - - @property - def action_space(self): - return gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) - - def __init__(self, physical_system=None): - self._angle = 0.0 - self._angle_index = None - super().__init__(physical_system) - - def set_physical_system(self, physical_system): - assert isinstance(physical_system.unwrapped, SCMLSystem) - assert isinstance(physical_system.electrical_motor, ThreePhaseMotor) - super().set_physical_system(physical_system) - self._angle_index = physical_system.state_names.index('epsilon') - return self - - def reset(self): - state = self._physical_system.reset() - return np.concatenate((state, [self._get_current_sum(state)])) - - def simulate(self, action): - state = self._physical_system.simulate(action) - return np.concatenate((state, [self._get_current_sum(state)])) - - def _get_epsilon(self, state): - return state[self._epsilon_idx] diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py new file mode 100644 index 00000000..9569a5b1 --- /dev/null +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -0,0 +1,58 @@ +import gym +import numpy as np + +import gym_electric_motor.physical_systems as ps +from gym_electric_motor.state_action_processors import StateActionProcessor + + +class DqToAbcActionProcessor(StateActionProcessor): + + @property + def action_space(self): + return gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) + + @staticmethod + def _transformation(self, action, angle): + return ps.ThreePhaseMotor.t_32(ps.ThreePhaseMotor.q_inv(action, angle)) + + def __init__(self, angle_name=None, physical_system=None): + self._angle = 0.0 + self._angle_index = None + self._omega_index = None + self._state = None + self._angle_name = angle_name + self._angle_advance = 0.0 + super().__init__(physical_system) + + def set_physical_system(self, physical_system): + assert isinstance(physical_system.unwrapped, ps.SCMLSystem) + assert isinstance(physical_system.electrical_motor, ps.ThreePhaseMotor) + super().set_physical_system(physical_system) + if self._angle_name is None: + if isinstance(physical_system.electric_motor, + (ps.PermanentMagnetSynchronousMotor, ps.SynchronousReluctanceMotor)): + self._angle_name = 'epsilon' + else: + self._angle_name = 'psi_angle' + assert self._angle_name in physical_system.state_names, \ + f'Angle {self._angle_name} not in the states of the physical system. Probably, a flux observer is required.' + self._angle_index = physical_system.state_names.index(self._angle_name) + self._omega_index = physical_system.state_names.index('omega') + if hasattr(physical_system.converter, 'dead_time') \ + and physical_system.converter.dead_time == True: + self._angle_advance = 1.5 + else: + self._angle_advance = 0.5 + + return self + + def simulate(self, action): + advanced_angle = self._state[self._angle_index] \ + + self._angle_advance * self._physical_system.tau * self._state[self._omega_index] + abc_action = self._transformation(action, advanced_angle) + self._state = self._physical_system.simulate(abc_action) + return self._state + + def reset(self, **kwargs): + self._state = self._physical_system.reset() + return self._state \ No newline at end of file diff --git a/gym_electric_motor/state_action_processors/flux_observer.py b/gym_electric_motor/state_action_processors/flux_observer.py index 57f72b2a..9629b435 100644 --- a/gym_electric_motor/state_action_processors/flux_observer.py +++ b/gym_electric_motor/state_action_processors/flux_observer.py @@ -7,9 +7,18 @@ class FluxObserver(StateActionProcessor): - def __init__(self, current_names=('i_sd', 'i_sq'), physical_system=None): + def __init__(self, current_names=('i_sa', 'i_sb', 'i_sc'), physical_system=None): super(FluxObserver, self).__init__(physical_system) self._current_indices = None + self._l_m = None # Main induction + self._l_r = None # Induction of the rotor + self._r_r = None # Rotor resistance + self._p = None # Pole pair number + self._i_s_idx = None + self._omega_idx = None + + # Integrated values of the flux for the two directions (Re: alpha, Im: beta) + self._integrated = np.complex(0, 0) self._current_names = current_names def set_physical_system(self, physical_system): @@ -19,21 +28,36 @@ def set_physical_system(self, physical_system): high = np.concatenate((physical_system.state_space.high, [1., 1.])) self.state_space = gym.spaces.Box(low, high, dtype=np.float64) self._current_indices = [physical_system.state_positions[name] for name in self._current_names] - current_limit = self._limit(physical_system.limits[self._current_indices]) - current_nominal_value = self._limit(physical_system.nominal_state[self._current_indices]) - self._limits = np.concatenate((physical_system.limits, [current_limit])) - self._nominal_state = np.concatenate((physical_system.nominal_state, [current_nominal_value])) - self._state_names = physical_system.state_names + ['i_sum'] + psi_limit = 100.0 + self._limits = np.concatenate((physical_system.limits, [psi_limit, np.pi])) + self._nominal_state = np.concatenate((physical_system.nominal_state, [psi_limit, np.pi])) + self._state_names = physical_system.state_names + ['psi_abs', 'psi_angle'] self._state_positions = {key: index for index, key in enumerate(self._state_names)} + mp = physical_system.electrical_motor.motor_parameter + self._l_m = mp['l_m'] # Main induction + self._l_r = mp['l_m'] + mp['l_sigr'] # Induction of the rotor + self._r_r = mp['r_r'] # Rotor resistance + self._p = mp['p'] # Pole pair number + + self._i_s_idx = [physical_system.state_positions[name] for name in self._current_names] + self._omega_idx = physical_system.state_positions['omega'] return self def reset(self): - state = self._physical_system.reset() - return np.concatenate((state, [self._get_current_sum(state)])) + self._integrated = np.complex(0, 0) def simulate(self, action): state = self._physical_system.simulate(action) - return np.concatenate((state, [self._get_current_sum(state)])) + i_s = state[self.i_s_idx] + omega = state[self.omega_idx] * self.p - def _get_current_sum(self, state): - return np.sum(state[self._current_indices]) + # Transform current into alpha, beta coordinates + [i_s_alpha, i_s_beta] = self.abc_to_alphabeta_transformation(i_s) + + # Calculate delta flux + delta = np.complex(i_s_alpha, i_s_beta) * self.r_r * self.l_m / self.l_r \ + - self._integrated * np.complex(self.r_r / self.l_r, -omega) + + # Integrate the flux + self._integrated += delta * self._physical_system.tau + return np.concatenate((state, [np.abs(self.integrated), np.angle(self.integrated)])) From 1cfbafcbaf0efa748310c7c4fa24c52613f87949 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 22 Nov 2021 22:14:11 +0100 Subject: [PATCH 06/79] First version of the state action processors --- .../classic_controllers.py | 20 ++++++++--------- .../classic_controllers_ind_motor_example.py | 14 +++++++----- .../abccont_sc_scim_env.py | 6 +++-- .../state_action_processors/__init__.py | 4 +++- .../dq_to_abc_action_processor.py | 11 +++++----- .../state_action_processors/flux_observer.py | 22 ++++++++++++------- 6 files changed, 45 insertions(+), 32 deletions(-) diff --git a/examples/classic_controllers/classic_controllers.py b/examples/classic_controllers/classic_controllers.py index 86a0e42c..53e749e6 100644 --- a/examples/classic_controllers/classic_controllers.py +++ b/examples/classic_controllers/classic_controllers.py @@ -117,7 +117,7 @@ def reference_states(environment, **controller_kwargs): def find_controller_type(environment, stages, **controller_kwargs): _stages = stages - if isinstance(environment.physical_system, DcMotorSystem): + if isinstance(environment.physical_system.unwrapped, DcMotorSystem): if type(stages) is list: if len(stages) > 1: if type(stages[0]) is list: @@ -135,7 +135,7 @@ def find_controller_type(environment, stages, **controller_kwargs): else: controller_type = stages _stages = [{'controller_type': stages}] - elif isinstance(environment.physical_system, SynchronousMotorSystem): + elif isinstance(environment.physical_system.unwrapped, SynchronousMotorSystem): if len(stages) == 2: if len(stages[1]) == 1 and 'i_sq' in controller_kwargs['ref_states']: controller_type = 'foc_controller' @@ -144,7 +144,7 @@ def find_controller_type(environment, stages, **controller_kwargs): else: controller_type = 'cascaded_foc_controller' - elif isinstance(environment.physical_system, SquirrelCageInductionMotorSystem): + elif isinstance(environment.physical_system.unwrapped, SquirrelCageInductionMotorSystem): if len(stages) == 2: if len(stages[1]) == 1 and 'i_sq' in controller_kwargs['ref_states']: controller_type = 'foc_rotor_flux_observer' @@ -153,7 +153,7 @@ def find_controller_type(environment, stages, **controller_kwargs): else: controller_type = 'cascaded_foc_rotor_flux_observer' - elif isinstance(environment.physical_system, DoublyFedInductionMotorSystem): + elif isinstance(environment.physical_system.unwrapped, DoublyFedInductionMotorSystem): if len(stages) == 2: if len(stages[1]) == 1 and 'i_sq' in controller_kwargs['ref_states']: controller_type = 'foc_rotor_flux_observer' @@ -171,7 +171,7 @@ def automated_controller_design(environment, **controller_kwargs): action_space_type = type(environment.action_space) ref_states = controller_kwargs['ref_states'] stages = [] - if isinstance(environment.physical_system, DcMotorSystem): # Checking type of motor + if isinstance(environment.physical_system.unwrapped, DcMotorSystem): # Checking type of motor if 'omega' in ref_states or 'torque' in ref_states: # Checking control task controller_type = 'cascaded_controller' @@ -200,7 +200,7 @@ def automated_controller_design(environment, **controller_kwargs): else: stages = [stages, [{'controller_type': 'three_point'}]] - elif isinstance(environment.physical_system, SynchronousMotorSystem): + elif isinstance(environment.physical_system.unwrapped, SynchronousMotorSystem): if 'i_sq' in ref_states or 'torque' in ref_states: # Checking control task controller_type = 'foc_controller' if 'i_sq' in ref_states else 'cascaded_foc_controller' if action_space_type is Discrete: @@ -217,7 +217,7 @@ def automated_controller_design(environment, **controller_kwargs): stages = [[{'controller_type': 'pi_controller'}, {'controller_type': 'pi_controller'}], [{'controller_type': 'pi_controller'}]] - elif isinstance(environment.physical_system, (SquirrelCageInductionMotorSystem, DoublyFedInductionMotorSystem)): + elif isinstance(environment.physical_system.unwrapped, (SquirrelCageInductionMotorSystem, DoublyFedInductionMotorSystem)): if 'i_sq' in ref_states or 'torque' in ref_states: controller_type = 'foc_rotor_flux_observer' if 'i_sq' in ref_states else 'cascaded_foc_rotor_flux_observer' if action_space_type is Discrete: @@ -260,13 +260,13 @@ def automated_gain(environment, stages, controller_type, _controllers, **control mp = environment.physical_system.electrical_motor.motor_parameter limits = environment.physical_system.limits omega_lim = limits[environment.state_names.index('omega')] - if isinstance(environment.physical_system, DcMotorSystem): + if isinstance(environment.physical_system.unwrapped, DcMotorSystem): i_a_lim = limits[environment.physical_system.CURRENTS_IDX[0]] i_e_lim = limits[environment.physical_system.CURRENTS_IDX[-1]] u_a_lim = limits[environment.physical_system.VOLTAGES_IDX[0]] u_e_lim = limits[environment.physical_system.VOLTAGES_IDX[-1]] - elif isinstance(environment.physical_system, SynchronousMotorSystem): + elif isinstance(environment.physical_system.unwrapped, SynchronousMotorSystem): i_sd_lim = limits[environment.state_names.index('i_sd')] i_sq_lim = limits[environment.state_names.index('i_sq')] u_sd_lim = limits[environment.state_names.index('u_sd')] @@ -286,7 +286,7 @@ def automated_gain(environment, stages, controller_type, _controllers, **control if isinstance(environment.physical_system.electrical_motor, DcSeriesMotor): mp['l'] = mp['l_a'] + mp['l_e'] - elif isinstance(environment.physical_system, DcMotorSystem): + elif isinstance(environment.physical_system.unwrapped, DcMotorSystem): mp['l'] = mp['l_a'] if 'automated_gain' not in controller_kwargs.keys() or automated_gain: diff --git a/examples/classic_controllers/classic_controllers_ind_motor_example.py b/examples/classic_controllers/classic_controllers_ind_motor_example.py index b237f2fa..4ecc22db 100644 --- a/examples/classic_controllers/classic_controllers_ind_motor_example.py +++ b/examples/classic_controllers/classic_controllers_ind_motor_example.py @@ -3,6 +3,7 @@ from external_plot import ExternalPlot import gym_electric_motor as gem from gym_electric_motor.visualization import MotorDashboard +from gym_electric_motor.state_action_processors import FluxObserver import numpy as np if __name__ == '__main__': @@ -24,13 +25,14 @@ env_id = action_type + '-' + control_type + '-' + motor_type + '-v0' # definition of the plotted variables - states = ['omega', 'torque', 'i_sd', 'i_sq', 'u_sd', 'u_sq'] - external_ref_plots = [ExternallyReferencedStatePlot(state) for state in states] - external_plot = [ExternalPlot(referenced=control_type != 'CC'), ExternalPlot(min=-np.pi, max=np.pi)] - external_ref_plots += external_plot + #states = ['omega', 'torque', 'i_sd', 'i_sq', 'u_sd', 'u_sq'] + #external_ref_plots = [ExternallyReferencedStatePlot(state) for state in states] + #external_plot = [ExternalPlot(referenced=control_type != 'CC'), ExternalPlot(min=-np.pi, max=np.pi)] + #external_ref_plots += external_plot # initialize the gym-electric-motor environment - env = gem.make(env_id, visualization=MotorDashboard(additional_plots=external_ref_plots)) + env = gem.make(env_id, state_action_processors=(FluxObserver(),), + visualization=MotorDashboard(state_plots=('omega', 'psi_abs', 'psi_angle'))) """ initialize the controller @@ -42,7 +44,7 @@ automated_gain (optional) if True (default), the controller will be tune automatically """ - controller = Controller.make(env, external_plot=external_ref_plots) + controller = Controller.make(env) state, reference = env.reset() diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_sc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_sc_scim_env.py index daa697f3..b179f94b 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_sc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_sc_scim_env.py @@ -92,7 +92,8 @@ class AbcContSpeedControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment) """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + state_action_processors=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), + calc_jacobian=True, tau=1e-4): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -152,5 +153,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/state_action_processors/__init__.py b/gym_electric_motor/state_action_processors/__init__.py index ecf6d737..c2675ba4 100644 --- a/gym_electric_motor/state_action_processors/__init__.py +++ b/gym_electric_motor/state_action_processors/__init__.py @@ -1,2 +1,4 @@ from .state_action_processor import StateActionProcessor -from .current_sum_processor import CurrentSumProcessor \ No newline at end of file +from .current_sum_processor import CurrentSumProcessor +from .flux_observer import FluxObserver +from .dq_to_abc_action_processor import DqToAbcActionProcessor diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py index 9569a5b1..1b6ec209 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -12,7 +12,7 @@ def action_space(self): return gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) @staticmethod - def _transformation(self, action, angle): + def _transformation(action, angle): return ps.ThreePhaseMotor.t_32(ps.ThreePhaseMotor.q_inv(action, angle)) def __init__(self, angle_name=None, physical_system=None): @@ -39,7 +39,7 @@ def set_physical_system(self, physical_system): self._angle_index = physical_system.state_names.index(self._angle_name) self._omega_index = physical_system.state_names.index('omega') if hasattr(physical_system.converter, 'dead_time') \ - and physical_system.converter.dead_time == True: + and physical_system.converter.dead_time is True: self._angle_advance = 1.5 else: self._angle_advance = 0.5 @@ -47,12 +47,13 @@ def set_physical_system(self, physical_system): return self def simulate(self, action): - advanced_angle = self._state[self._angle_index] \ - + self._angle_advance * self._physical_system.tau * self._state[self._omega_index] + advanced_angle = self._state[self._angle_index] * self._physical_system.limits[self._angle_index] \ + + self._angle_advance * self._physical_system.tau \ + * self._state[self._omega_index] * self._physical_system.limits[self._omega_index] abc_action = self._transformation(action, advanced_angle) self._state = self._physical_system.simulate(abc_action) return self._state def reset(self, **kwargs): self._state = self._physical_system.reset() - return self._state \ No newline at end of file + return self._state diff --git a/gym_electric_motor/state_action_processors/flux_observer.py b/gym_electric_motor/state_action_processors/flux_observer.py index 9629b435..cf727171 100644 --- a/gym_electric_motor/state_action_processors/flux_observer.py +++ b/gym_electric_motor/state_action_processors/flux_observer.py @@ -21,6 +21,10 @@ def __init__(self, current_names=('i_sa', 'i_sb', 'i_sc'), physical_system=None) self._integrated = np.complex(0, 0) self._current_names = current_names + @staticmethod + def _abc_to_alphabeta_transformation(i_s): + return gem.physical_systems.electric_motors.ThreePhaseMotor.t_23(i_s) + def set_physical_system(self, physical_system): assert isinstance(physical_system.electrical_motor, gem.physical_systems.electric_motors.InductionMotor) super().set_physical_system(physical_system) @@ -28,7 +32,7 @@ def set_physical_system(self, physical_system): high = np.concatenate((physical_system.state_space.high, [1., 1.])) self.state_space = gym.spaces.Box(low, high, dtype=np.float64) self._current_indices = [physical_system.state_positions[name] for name in self._current_names] - psi_limit = 100.0 + psi_limit = 1.0 self._limits = np.concatenate((physical_system.limits, [psi_limit, np.pi])) self._nominal_state = np.concatenate((physical_system.nominal_state, [psi_limit, np.pi])) self._state_names = physical_system.state_names + ['psi_abs', 'psi_angle'] @@ -45,19 +49,21 @@ def set_physical_system(self, physical_system): def reset(self): self._integrated = np.complex(0, 0) + return np.concatenate((super().reset(), [0.0, 0.0])) def simulate(self, action): - state = self._physical_system.simulate(action) - i_s = state[self.i_s_idx] - omega = state[self.omega_idx] * self.p + state_norm = self._physical_system.simulate(action) + state = state_norm * self._physical_system.limits + i_s = state[self._i_s_idx] + omega = state[self._omega_idx] * self._p # Transform current into alpha, beta coordinates - [i_s_alpha, i_s_beta] = self.abc_to_alphabeta_transformation(i_s) + [i_s_alpha, i_s_beta] = self._abc_to_alphabeta_transformation(i_s) # Calculate delta flux - delta = np.complex(i_s_alpha, i_s_beta) * self.r_r * self.l_m / self.l_r \ - - self._integrated * np.complex(self.r_r / self.l_r, -omega) + delta = np.complex(i_s_alpha, i_s_beta) * self._r_r * self._l_m / self._l_r \ + - self._integrated * np.complex(self._r_r / self._l_r, -omega) # Integrate the flux self._integrated += delta * self._physical_system.tau - return np.concatenate((state, [np.abs(self.integrated), np.angle(self.integrated)])) + return np.concatenate((state, [np.abs(self._integrated), np.angle(self._integrated)])) / self._limits From bf5c4e064ca2a7f7e7f0a66bb9b3c40465e18933 Mon Sep 17 00:00:00 2001 From: Arne Date: Tue, 23 Nov 2021 22:31:57 +0100 Subject: [PATCH 07/79] First version of the state action processors --- .../dead_time_processor.py | 46 ++++++++++++++ .../dq_to_abc_action_processor.py | 2 +- .../state_action_processor.py | 4 +- .../state_noise_processor.py | 63 +++++++++++-------- 4 files changed, 87 insertions(+), 28 deletions(-) create mode 100644 gym_electric_motor/state_action_processors/dead_time_processor.py diff --git a/gym_electric_motor/state_action_processors/dead_time_processor.py b/gym_electric_motor/state_action_processors/dead_time_processor.py new file mode 100644 index 00000000..5ffdb4fb --- /dev/null +++ b/gym_electric_motor/state_action_processors/dead_time_processor.py @@ -0,0 +1,46 @@ +from collections import deque +import numpy as np + +import gym.spaces +from gym_electric_motor.state_action_processors import StateActionProcessor + + +class DeadTimeProcessor(StateActionProcessor): + + @property + def dead_time(self): + return True + + def __init__(self, steps=1, reset_action=None, physical_system=None): + self._reset_action = reset_action + self._action_deque = deque(maxlen=steps) + super().__init__(physical_system) + + def set_physical_system(self, physical_system): + + if self._reset_action is None: + action_space = physical_system.action_space + if isinstance(action_space, gym.spaces.Discrete): + self._reset_action = 0 + elif isinstance(action_space, gym.spaces.MultiDiscrete): + self._reset_action = np.zeros_like(action_space.nvec) + elif isinstance(action_space, gym.spaces.Box): + self._reset_action = np.zeros(action_space.shape, dtype=np.float64) + else: + raise AssertionError( + f'Action Space {action_space} of type {type(action_space)} unsupported.' + 'Only Discrete / MultiDiscrete and Box allowed for the dead time processor.' + ) + + def reset(self): + state = super().reset() + self._action_deque.clear() + self._action_deque.extend([self._reset_action] * self._action_deque.maxlen) + return state + + def simulate(self, action): + active_action = self._action_deque.pop() + self._action_deque.appendleft(action) + return self._physical_system.simulate(active_action) + + diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py index 1b6ec209..09bf7b64 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -29,7 +29,7 @@ def set_physical_system(self, physical_system): assert isinstance(physical_system.electrical_motor, ps.ThreePhaseMotor) super().set_physical_system(physical_system) if self._angle_name is None: - if isinstance(physical_system.electric_motor, + if isinstance(physical_system.electrical_motor, (ps.PermanentMagnetSynchronousMotor, ps.SynchronousReluctanceMotor)): self._angle_name = 'epsilon' else: diff --git a/gym_electric_motor/state_action_processors/state_action_processor.py b/gym_electric_motor/state_action_processors/state_action_processor.py index 6279eaff..60c6b3b6 100644 --- a/gym_electric_motor/state_action_processors/state_action_processor.py +++ b/gym_electric_motor/state_action_processors/state_action_processor.py @@ -63,7 +63,8 @@ def state_names(self): return self._state_names def __init__(self, physical_system=None): - super().__init__(None, None, (), None) + gem.core.PhysicalSystem.__init__(self, None, None, (), None) + gem.core.RandomComponent.__init__(self) self._physical_system = physical_system self._limits = None self._nominal_state = None @@ -89,6 +90,7 @@ def simulate(self, action): return self._physical_system.simulate(action) def reset(self, **kwargs): + self.next_generator() return self._physical_system.reset(**kwargs) def __str__(self): diff --git a/gym_electric_motor/state_action_processors/state_noise_processor.py b/gym_electric_motor/state_action_processors/state_noise_processor.py index 401bd86f..41d91f0d 100644 --- a/gym_electric_motor/state_action_processors/state_noise_processor.py +++ b/gym_electric_motor/state_action_processors/state_noise_processor.py @@ -1,39 +1,50 @@ -import gym -import numpy as np - from gym_electric_motor.state_action_processors import StateActionProcessor -class CurrentSumProcessor(StateActionProcessor): +class StateNoiseProcessor(StateActionProcessor): + + @property + def random_kwargs(self): + return self._random_kwargs - def __init__(self, currents, limit='max', physical_system=None): - self._currents = currents - assert limit in ['max', 'sum'] - self._limit = max if limit == 'max' else np.sum - self._current_indices = [] + @random_kwargs.setter + def random_kwargs(self, value): + self._random_kwargs = dict(value) + + def __init__(self, states, random_dist='normal', random_kwargs=(), random_length=1000, physical_system=None): + self._random_kwargs = dict(random_kwargs) + self._random_length = int(random_length) + self._random_pointer = 0 + self._noise = None + self._states = states + self._random_dist = random_dist + self._state_indices = [] super().__init__(physical_system) + assert hasattr(self._random_generator, random_dist), \ + f'The numpy random number generator has no distribution {random_dist}.'\ + 'Check https://numpy.org/doc/stable/reference/random/generator.html#distributions for distributions.' def set_physical_system(self, physical_system): super().set_physical_system(physical_system) - low = np.concatenate((physical_system.state_space.low, [-1.])) - high = np.concatenate((physical_system.state_space.high, [1.])) - self.state_space = gym.spaces.Box(low, high, dtype=np.float64) - self._current_indices = [physical_system.state_positions[current] for current in self._currents] - current_limit = self._limit(physical_system.limits[self._current_indices]) - current_nominal_value = self._limit(physical_system.nominal_state[self._current_indices]) - self._limits = np.concatenate((physical_system.limits, [current_limit])) - self._nominal_state = np.concatenate((physical_system.nominal_state, [current_nominal_value])) - self._state_names = physical_system.state_names + ['i_sum'] - self._state_positions = {key: index for index, key in enumerate(self._state_names)} + self._state_indices = [physical_system.state_positions[state_name] for state_name in self._states] return self def reset(self): - state = self._physical_system.reset() - return np.concatenate((state, [self._get_current_sum(state)])) + state = super().reset() + self._new_noise() + return self._add_noise(state) def simulate(self, action): - state = self._physical_system.simulate(action) - return np.concatenate((state, [self._get_current_sum(state)])) - - def _get_current_sum(self, state): - return np.sum(state[self._current_indices]) + if self._random_pointer >= self._random_length: + self._new_noise() + return self._add_noise(self._physical_system.simulate(action)) + + def _add_noise(self, state): + state[self._state_indices] = state[self._state_indices] + self._noise[self._random_pointer] + self._random_pointer += 1 + return state + + def _new_noise(self): + self._random_pointer = 0 + fct = getattr(self._random_generator, self._random_dist) + self._noise = fct(size=(self._random_length, len(self._state)), **self._random_kwargs) From b35b1b73cfa8c3a8db6c802338b780db9574b104 Mon Sep 17 00:00:00 2001 From: Arne Date: Tue, 30 Nov 2021 19:52:26 +0100 Subject: [PATCH 08/79] Added Processors to env initialization --- .../classic_controllers_dc_motor_example.py | 4 ++-- .../classic_controllers_ind_motor_example.py | 2 +- .../classic_controllers_synch_motor_example.py | 6 +++--- .../gym_dcm/extex_dc_motor_env/cont_cc_extex_dc_env.py | 7 +++++-- .../gym_dcm/extex_dc_motor_env/cont_sc_extex_dc_env.py | 7 +++++-- .../gym_dcm/extex_dc_motor_env/cont_tc_extex_dc_env.py | 7 +++++-- .../gym_dcm/extex_dc_motor_env/finite_cc_extex_dc_env.py | 7 +++++-- .../gym_dcm/extex_dc_motor_env/finite_sc_extex_dc_env.py | 7 +++++-- .../gym_dcm/extex_dc_motor_env/finite_tc_extex_dc_env.py | 7 +++++-- .../gym_dcm/permex_dc_motor_env/cont_cc_permex_dc_env.py | 7 +++++-- .../gym_dcm/permex_dc_motor_env/cont_sc_permex_dc_env.py | 7 +++++-- .../gym_dcm/permex_dc_motor_env/cont_tc_permex_dc_env.py | 7 +++++-- .../permex_dc_motor_env/finite_cc_permex_dc_env.py | 7 +++++-- .../permex_dc_motor_env/finite_sc_permex_dc_env.py | 7 +++++-- .../permex_dc_motor_env/finite_tc_permex_dc_env.py | 7 +++++-- .../gym_dcm/series_dc_motor_env/cont_cc_series_dc_env.py | 7 +++++-- .../gym_dcm/series_dc_motor_env/cont_sc_series_dc_env.py | 7 +++++-- .../gym_dcm/series_dc_motor_env/cont_tc_series_dc_env.py | 7 +++++-- .../series_dc_motor_env/finite_cc_series_dc_env.py | 7 +++++-- .../series_dc_motor_env/finite_sc_series_dc_env.py | 7 +++++-- .../series_dc_motor_env/finite_tc_series_dc_env.py | 7 +++++-- .../gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py | 6 ++++-- .../gym_dcm/shunt_dc_motor_env/cont_sc_shunt_dc_env.py | 8 ++++++-- .../gym_dcm/shunt_dc_motor_env/cont_tc_shunt_dc_env.py | 8 ++++++-- .../gym_dcm/shunt_dc_motor_env/finite_cc_shunt_dc_env.py | 8 ++++++-- .../gym_dcm/shunt_dc_motor_env/finite_sc_shunt_dc_env.py | 8 ++++++-- .../gym_dcm/shunt_dc_motor_env/finite_tc_shunt_dc_env.py | 8 ++++++-- .../abccont_cc_scim_env.py | 8 ++++++-- .../abccont_sc_scim_env.py | 2 ++ .../abccont_tc_scim_env.py | 8 ++++++-- .../dqcont_cc_scim_env.py | 8 ++++++-- .../dqcont_sc_scim_env.py | 8 ++++++-- .../dqcont_tc_scim_env.py | 8 ++++++-- .../finite_cc_scim_env.py | 8 ++++++-- .../finite_sc_scim_env.py | 8 ++++++-- .../finite_tc_scim_env.py | 8 ++++++-- gym_electric_motor/envs/gym_pmsm/abccont_cc_pmsm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_pmsm/abccont_sc_pmsm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_pmsm/abccont_tc_pmsm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_pmsm/dqcont_cc_pmsm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_pmsm/dqcont_sc_pmsm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_pmsm/dqcont_tc_pmsm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_pmsm/finite_cc_pmsm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_pmsm/finite_sc_pmsm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_pmsm/finite_tc_pmsm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_synrm/abccont_cc_synrm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_synrm/abccont_sc_synrm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_synrm/abccont_tc_synrm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_synrm/dqcont_cc_synrm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_synrm/dqcont_sc_synrm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_synrm/dqcont_tc_synrm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_synrm/finite_cc_synrm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_synrm/finite_sc_synrm_env.py | 8 ++++++-- gym_electric_motor/envs/gym_synrm/finite_tc_synrm_env.py | 8 ++++++-- 54 files changed, 288 insertions(+), 106 deletions(-) diff --git a/examples/classic_controllers/classic_controllers_dc_motor_example.py b/examples/classic_controllers/classic_controllers_dc_motor_example.py index d8767cc5..c0e6a458 100644 --- a/examples/classic_controllers/classic_controllers_dc_motor_example.py +++ b/examples/classic_controllers/classic_controllers_dc_motor_example.py @@ -20,8 +20,8 @@ """ motor_type = 'PermExDc' - control_type = 'TC' - action_type = 'Cont' + control_type = 'SC' + action_type = 'F' motor = action_type + '-' + control_type + '-' + motor_type + '-v0' diff --git a/examples/classic_controllers/classic_controllers_ind_motor_example.py b/examples/classic_controllers/classic_controllers_ind_motor_example.py index 4ecc22db..ab957c7e 100644 --- a/examples/classic_controllers/classic_controllers_ind_motor_example.py +++ b/examples/classic_controllers/classic_controllers_ind_motor_example.py @@ -19,7 +19,7 @@ """ motor_type = 'SCIM' - control_type = 'SC' + control_type = 'TC' action_type = 'AbcCont' env_id = action_type + '-' + control_type + '-' + motor_type + '-v0' diff --git a/examples/classic_controllers/classic_controllers_synch_motor_example.py b/examples/classic_controllers/classic_controllers_synch_motor_example.py index de687d49..388d6e2b 100644 --- a/examples/classic_controllers/classic_controllers_synch_motor_example.py +++ b/examples/classic_controllers/classic_controllers_synch_motor_example.py @@ -18,9 +18,9 @@ 'Finite' Discrete Action Space """ - motor_type = 'PMSM' - control_type = 'TC' - action_type = 'AbcCont' + motor_type = 'SynRM' + control_type = 'SC' + action_type = 'Finite' env_id = action_type + '-' + control_type + '-' + motor_type + '-v0' diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_cc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_cc_extex_dc_env.py index bd3f9187..577f9572 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_cc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_cc_extex_dc_env.py @@ -86,7 +86,7 @@ class ContCurrentControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,6 +108,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -156,5 +158,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization, MotorDashboard, dict(state_plots=('i_a', 'i_e',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_sc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_sc_extex_dc_env.py index 3b6b9d50..4429ca21 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_sc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_sc_extex_dc_env.py @@ -86,7 +86,7 @@ class ContSpeedControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,6 +108,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -149,5 +151,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_tc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_tc_extex_dc_env.py index dfe1621a..32ee4435 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_tc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_tc_extex_dc_env.py @@ -86,7 +86,7 @@ class ContTorqueControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e',), calc_jacobian=True, tau=1e-4): + constraints=('i_a', 'i_e',), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,6 +108,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -149,5 +151,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_cc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_cc_extex_dc_env.py index 3be98c1d..af5d5ee6 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_cc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_cc_extex_dc_env.py @@ -86,7 +86,7 @@ class FiniteCurrentControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,6 +108,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -153,5 +155,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve dict(state_plots=('i_a', 'i_e',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_sc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_sc_extex_dc_env.py index 6c7e7ff1..1d5c6ba2 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_sc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_sc_extex_dc_env.py @@ -86,7 +86,7 @@ class FiniteSpeedControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,6 +108,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -150,5 +152,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_tc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_tc_extex_dc_env.py index 955d4738..c31ded16 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_tc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_tc_extex_dc_env.py @@ -86,7 +86,7 @@ class FiniteTorqueControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5,state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,6 +108,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -148,5 +150,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('torque',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_cc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_cc_permex_dc_env.py index 0b1b6433..39705204 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_cc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_cc_permex_dc_env.py @@ -87,7 +87,7 @@ class ContCurrentControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): def __init__( self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-4 + constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=() ): """ Args: @@ -110,6 +110,8 @@ def __init__( tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -146,5 +148,6 @@ def __init__( visualization, MotorDashboard, dict(state_plots=('i',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_sc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_sc_permex_dc_env.py index 3c80aae2..1e4af44b 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_sc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_sc_permex_dc_env.py @@ -86,7 +86,7 @@ class ContSpeedControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-4): + constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,6 +108,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -145,5 +147,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_tc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_tc_permex_dc_env.py index 71b12e0d..c4e4c8f3 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_tc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_tc_permex_dc_env.py @@ -87,7 +87,7 @@ class ContTorqueControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-4): + constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +109,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -148,5 +150,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_cc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_cc_permex_dc_env.py index 70167b24..447dce5e 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_cc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_cc_permex_dc_env.py @@ -87,7 +87,7 @@ class FiniteCurrentControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment) def __init__( self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-5 + constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=() ): """ Args: @@ -110,6 +110,8 @@ def __init__( tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -145,5 +147,6 @@ def __init__( ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('i',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_sc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_sc_permex_dc_env.py index 13053189..d7a963ce 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_sc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_sc_permex_dc_env.py @@ -87,7 +87,7 @@ class FiniteSpeedControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): def __init__( self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-5 + constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=() ): """ Args: @@ -110,6 +110,8 @@ def __init__( tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -148,5 +150,6 @@ def __init__( ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_tc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_tc_permex_dc_env.py index 264c8e70..2cf523bf 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_tc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_tc_permex_dc_env.py @@ -87,7 +87,7 @@ class FiniteTorqueControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): def __init__( self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-5 + constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=() ): """ Args: @@ -110,6 +110,8 @@ def __init__( tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -145,6 +147,7 @@ def __init__( ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('torque',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_cc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_cc_series_dc_env.py index 55f4d8ec..ded30773 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_cc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_cc_series_dc_env.py @@ -86,7 +86,7 @@ class ContCurrentControlDcSeriesMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-4): + constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +109,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -144,5 +146,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization, MotorDashboard, dict(state_plots=('i',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_sc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_sc_series_dc_env.py index 184879aa..d7cd6358 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_sc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_sc_series_dc_env.py @@ -87,7 +87,7 @@ class ContSpeedControlDcSeriesMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-4): + constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +109,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -146,6 +148,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_tc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_tc_series_dc_env.py index a8e40a1f..dc93d994 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_tc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_tc_series_dc_env.py @@ -87,7 +87,7 @@ class ContTorqueControlDcSeriesMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-4): + constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +109,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -143,5 +145,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('torque',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_cc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_cc_series_dc_env.py index 4c226c9f..a53d459b 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_cc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_cc_series_dc_env.py @@ -86,7 +86,7 @@ class FiniteCurrentControlDcSeriesMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-5): + constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,6 +108,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -142,5 +144,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('i',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_sc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_sc_series_dc_env.py index fab72d99..68619bc3 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_sc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_sc_series_dc_env.py @@ -86,7 +86,7 @@ class FiniteSpeedControlDcSeriesMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-5): + constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,6 +108,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -145,6 +147,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_tc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_tc_series_dc_env.py index 3c61d5ec..dfe17b7d 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_tc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_tc_series_dc_env.py @@ -86,7 +86,7 @@ class FiniteTorqueControlDcSeriesMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-5): + constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,6 +108,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -144,5 +146,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py index 3a94dc85..a8beb128 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py @@ -88,7 +88,7 @@ class ContCurrentControlDcShuntMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -110,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -148,5 +150,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=(CurrentSumProcessor(('i_a', 'i_e')),) + state_action_processors=tuple(state_action_processors) + (CurrentSumProcessor(('i_a', 'i_e')),) ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_sc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_sc_shunt_dc_env.py index 725691f0..f9ea6ec3 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_sc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_sc_shunt_dc_env.py @@ -1,6 +1,7 @@ from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ ElectricMotorVisualization from gym_electric_motor.physical_systems.physical_systems import DcMotorSystem +from gym_electric_motor.state_action_processors import CurrentSumProcessor from gym_electric_motor.visualization import MotorDashboard from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator from gym_electric_motor import physical_systems as ps @@ -87,7 +88,7 @@ class ContSpeedControlDcShuntMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -147,5 +150,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=tuple(state_action_processors) + (CurrentSumProcessor(('i_a', 'i_e')),) ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_tc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_tc_shunt_dc_env.py index 6ef448c0..10be31c6 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_tc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_tc_shunt_dc_env.py @@ -6,6 +6,7 @@ from gym_electric_motor import physical_systems as ps from gym_electric_motor.reward_functions import WeightedSumOfErrors from gym_electric_motor.utils import initialize +from gym_electric_motor.state_action_processors import CurrentSumProcessor class ContTorqueControlDcShuntMotorEnv(ElectricMotorEnvironment): @@ -87,7 +88,7 @@ class ContTorqueControlDcShuntMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -151,5 +154,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=tuple(state_action_processors) + (CurrentSumProcessor(('i_a', 'i_e')),) ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_cc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_cc_shunt_dc_env.py index a4ff6a53..93708016 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_cc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_cc_shunt_dc_env.py @@ -1,6 +1,7 @@ from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ ElectricMotorVisualization from gym_electric_motor.physical_systems.physical_systems import DcMotorSystem +from gym_electric_motor.state_action_processors import CurrentSumProcessor from gym_electric_motor.visualization import MotorDashboard from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator from gym_electric_motor import physical_systems as ps @@ -87,7 +88,7 @@ class FiniteCurrentControlDcShuntMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -145,5 +148,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve dict(state_plots=('i_a', 'i_e',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=tuple(state_action_processors) + (CurrentSumProcessor(('i_a', 'i_e')),) ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_sc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_sc_shunt_dc_env.py index ab06b5e0..9b0dc60b 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_sc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_sc_shunt_dc_env.py @@ -1,6 +1,7 @@ from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ ElectricMotorVisualization from gym_electric_motor.physical_systems.physical_systems import DcMotorSystem +from gym_electric_motor.state_action_processors import CurrentSumProcessor from gym_electric_motor.visualization import MotorDashboard from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator from gym_electric_motor import physical_systems as ps @@ -87,7 +88,7 @@ class FiniteSpeedControlDcShuntMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -147,5 +150,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=tuple(state_action_processors) + (CurrentSumProcessor(('i_a', 'i_e')),) ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_tc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_tc_shunt_dc_env.py index 5a4f7628..bb52fb41 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_tc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_tc_shunt_dc_env.py @@ -1,6 +1,7 @@ from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ ElectricMotorVisualization from gym_electric_motor.physical_systems.physical_systems import DcMotorSystem +from gym_electric_motor.state_action_processors import CurrentSumProcessor from gym_electric_motor.visualization import MotorDashboard from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator from gym_electric_motor import physical_systems as ps @@ -87,7 +88,7 @@ class FiniteTorqueControlDcShuntMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -143,5 +146,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('torque',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=tuple(state_action_processors) + (CurrentSumProcessor(('i_a', 'i_e')),) ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_cc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_cc_scim_env.py index 2768b4a2..871fca01 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_cc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_cc_scim_env.py @@ -92,7 +92,8 @@ class AbcContCurrentControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironmen """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -114,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -161,5 +164,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_sc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_sc_scim_env.py index b179f94b..2418d8aa 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_sc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_sc_scim_env.py @@ -115,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_tc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_tc_scim_env.py index a0aae0fc..0de74cf8 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_tc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_tc_scim_env.py @@ -92,7 +92,8 @@ class AbcContTorqueControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -114,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -156,5 +159,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_cc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_cc_scim_env.py index 553d933a..23767120 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_cc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_cc_scim_env.py @@ -92,7 +92,8 @@ class DqContCurrentControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -114,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -161,5 +164,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_sc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_sc_scim_env.py index 119267f7..7e916602 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_sc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_sc_scim_env.py @@ -92,7 +92,8 @@ class DqContSpeedControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -114,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -152,5 +155,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_tc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_tc_scim_env.py index 9d39c7f0..4da1127e 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_tc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_tc_scim_env.py @@ -92,7 +92,8 @@ class DqContTorqueControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment) """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -114,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -156,5 +159,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_cc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_cc_scim_env.py index 2a0bada0..cbec2251 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_cc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_cc_scim_env.py @@ -92,7 +92,8 @@ class FiniteCurrentControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -114,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -160,5 +163,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_sc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_sc_scim_env.py index d8294aa6..8179dd05 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_sc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_sc_scim_env.py @@ -92,7 +92,8 @@ class FiniteSpeedControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -114,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -151,5 +154,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_tc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_tc_scim_env.py index 972e74fc..d204370d 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_tc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_tc_scim_env.py @@ -92,7 +92,8 @@ class FiniteTorqueControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment) """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -114,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -149,5 +152,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_pmsm/abccont_cc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/abccont_cc_pmsm_env.py index b108853b..d2a05ec1 100644 --- a/gym_electric_motor/envs/gym_pmsm/abccont_cc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/abccont_cc_pmsm_env.py @@ -87,7 +87,8 @@ class AbcContCurrentControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvir """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -156,5 +159,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_pmsm/abccont_sc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/abccont_sc_pmsm_env.py index 07deb054..51f969ad 100644 --- a/gym_electric_motor/envs/gym_pmsm/abccont_sc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/abccont_sc_pmsm_env.py @@ -87,7 +87,8 @@ class AbcContSpeedControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviron """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -146,5 +149,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_pmsm/abccont_tc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/abccont_tc_pmsm_env.py index 2bc6577d..4f142aed 100644 --- a/gym_electric_motor/envs/gym_pmsm/abccont_tc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/abccont_tc_pmsm_env.py @@ -87,7 +87,8 @@ class AbcContTorqueControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviro """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -151,5 +154,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_pmsm/dqcont_cc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/dqcont_cc_pmsm_env.py index c5d8b8ac..8c15b195 100644 --- a/gym_electric_motor/envs/gym_pmsm/dqcont_cc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/dqcont_cc_pmsm_env.py @@ -87,7 +87,8 @@ class DqContCurrentControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviro """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -156,5 +159,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_pmsm/dqcont_sc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/dqcont_sc_pmsm_env.py index d45c1bf5..030c3d03 100644 --- a/gym_electric_motor/envs/gym_pmsm/dqcont_sc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/dqcont_sc_pmsm_env.py @@ -87,7 +87,8 @@ class DqContSpeedControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironm """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -146,5 +149,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_pmsm/dqcont_tc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/dqcont_tc_pmsm_env.py index 7aa0b901..630f27f9 100644 --- a/gym_electric_motor/envs/gym_pmsm/dqcont_tc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/dqcont_tc_pmsm_env.py @@ -87,7 +87,8 @@ class DqContTorqueControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviron """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -151,5 +154,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_pmsm/finite_cc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/finite_cc_pmsm_env.py index 3c3732ac..f1bcc47a 100644 --- a/gym_electric_motor/envs/gym_pmsm/finite_cc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/finite_cc_pmsm_env.py @@ -87,7 +87,8 @@ class FiniteCurrentControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviro """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -155,5 +158,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_pmsm/finite_sc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/finite_sc_pmsm_env.py index b45890f6..66f1d08b 100644 --- a/gym_electric_motor/envs/gym_pmsm/finite_sc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/finite_sc_pmsm_env.py @@ -87,7 +87,8 @@ class FiniteSpeedControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironm """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -145,5 +148,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_pmsm/finite_tc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/finite_tc_pmsm_env.py index ea573744..517ec1ba 100644 --- a/gym_electric_motor/envs/gym_pmsm/finite_tc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/finite_tc_pmsm_env.py @@ -87,7 +87,8 @@ class FiniteTorqueControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviron """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -144,5 +147,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_synrm/abccont_cc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/abccont_cc_synrm_env.py index 3d4a81a1..58fa3245 100644 --- a/gym_electric_motor/envs/gym_synrm/abccont_cc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/abccont_cc_synrm_env.py @@ -87,7 +87,8 @@ class AbcContCurrentControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironmen """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -156,5 +159,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_synrm/abccont_sc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/abccont_sc_synrm_env.py index e2195a86..8f7cce39 100644 --- a/gym_electric_motor/envs/gym_synrm/abccont_sc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/abccont_sc_synrm_env.py @@ -87,7 +87,8 @@ class AbcContSpeedControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment) """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -146,5 +149,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_synrm/abccont_tc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/abccont_tc_synrm_env.py index 49211e62..882b4011 100644 --- a/gym_electric_motor/envs/gym_synrm/abccont_tc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/abccont_tc_synrm_env.py @@ -87,7 +87,8 @@ class AbcContTorqueControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -151,5 +154,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_synrm/dqcont_cc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/dqcont_cc_synrm_env.py index 0df19818..9d04a860 100644 --- a/gym_electric_motor/envs/gym_synrm/dqcont_cc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/dqcont_cc_synrm_env.py @@ -87,7 +87,8 @@ class DqContCurrentControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -156,5 +159,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_synrm/dqcont_sc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/dqcont_sc_synrm_env.py index 4e432900..b8dfa7dc 100644 --- a/gym_electric_motor/envs/gym_synrm/dqcont_sc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/dqcont_sc_synrm_env.py @@ -87,7 +87,8 @@ class DqContSpeedControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -146,5 +149,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_synrm/dqcont_tc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/dqcont_tc_synrm_env.py index 2339b7c3..d73a99ea 100644 --- a/gym_electric_motor/envs/gym_synrm/dqcont_tc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/dqcont_tc_synrm_env.py @@ -87,7 +87,8 @@ class DqContTorqueControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment) """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -151,5 +154,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_synrm/finite_cc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/finite_cc_synrm_env.py index 709fe65e..eb11f2b0 100644 --- a/gym_electric_motor/envs/gym_synrm/finite_cc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/finite_cc_synrm_env.py @@ -87,7 +87,8 @@ class FiniteCurrentControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -155,5 +158,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_synrm/finite_sc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/finite_sc_synrm_env.py index f5584beb..7be3dfa1 100644 --- a/gym_electric_motor/envs/gym_synrm/finite_sc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/finite_sc_synrm_env.py @@ -87,7 +87,8 @@ class FiniteSpeedControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -145,5 +148,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_synrm/finite_tc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/finite_tc_synrm_env.py index 8b99fc03..e6f4533e 100644 --- a/gym_electric_motor/envs/gym_synrm/finite_tc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/finite_tc_synrm_env.py @@ -87,7 +87,8 @@ class FiniteTorqueControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment) """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,6 +110,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -144,5 +147,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) From 597392e4315f33443fc6836cff382eb896866742 Mon Sep 17 00:00:00 2001 From: Arne Date: Sun, 5 Dec 2021 16:01:16 +0100 Subject: [PATCH 09/79] CosSin Processor and documentation for processors --- docs/conf.py | 4 ++ .../state_action_processors/flux_observer.rst | 6 ++ .../state_action_processor.rst | 1 + .../cos_sin_processor.py | 55 +++++++++++++++++++ .../current_sum_processor.py | 30 +++++++++- .../dead_time_processor.py | 53 +++++++++++++++--- .../dq_to_abc_action_processor.py | 54 +++++++++++++----- .../state_action_processors/flux_observer.py | 24 +++++++- .../state_action_processor.py | 4 +- 9 files changed, 201 insertions(+), 30 deletions(-) create mode 100644 docs/parts/state_action_processors/flux_observer.rst create mode 100644 gym_electric_motor/state_action_processors/cos_sin_processor.py diff --git a/docs/conf.py b/docs/conf.py index 3de173e9..86968564 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -114,6 +114,9 @@ # -- Options for LaTeX output ------------------------------------------------ +latex_packages = r''' +\usepackage{amsmath} +''' latex_elements = { # The paper size ('letterpaper' or 'a4paper'). @@ -131,6 +134,7 @@ # Latex figure (float) alignment # # 'figure_align': 'htbp', + 'packages': latex_packages } # Grouping the document tree into LaTeX files. List of tuples diff --git a/docs/parts/state_action_processors/flux_observer.rst b/docs/parts/state_action_processors/flux_observer.rst new file mode 100644 index 00000000..afddec37 --- /dev/null +++ b/docs/parts/state_action_processors/flux_observer.rst @@ -0,0 +1,6 @@ +FluxObserver +##################### + +.. autoclass:: gym_electric_motor.state_action_processors.FluxObserver + :members: + :inherited-members: diff --git a/docs/parts/state_action_processors/state_action_processor.rst b/docs/parts/state_action_processors/state_action_processor.rst index 32fa698f..18be1f73 100644 --- a/docs/parts/state_action_processors/state_action_processor.rst +++ b/docs/parts/state_action_processors/state_action_processor.rst @@ -6,6 +6,7 @@ State Action Processor :caption: Available StateActionProcessors: current_sum_processor + flux_observer State Action Processor Base Class '''''''''''''''''''''''''''''''''' diff --git a/gym_electric_motor/state_action_processors/cos_sin_processor.py b/gym_electric_motor/state_action_processors/cos_sin_processor.py new file mode 100644 index 00000000..bf0f8c72 --- /dev/null +++ b/gym_electric_motor/state_action_processors/cos_sin_processor.py @@ -0,0 +1,55 @@ +import gym +import numpy as np + +from gym_electric_motor.state_action_processors import StateActionProcessor + + +class CosSinProcessor(StateActionProcessor): + """Adds ``cos(angle)`` and ``sin(angle)`` states to the systems state vector that are the cosine and sine of a + certain systems state. + """ + + def __init__(self, angle='epsilon', physical_system=None): + """ + Args: + angle(string): Name of the state whose cosine and sine will be added to the systems state vector. + Default: ``'epsilon'`` + physical_system(PhysicalSystem(optional): Inner system of this processor. + """ + self._angle = angle + self._angle_index = None + super().__init__(physical_system) + + def set_physical_system(self, physical_system): + # Docstring of super class + super().set_physical_system(physical_system) + low = np.concatenate((physical_system.state_space.low, [-1., -1.])) + high = np.concatenate((physical_system.state_space.high, [1., 1.])) + self.state_space = gym.spaces.Box(low, high, dtype=np.float64) + self._angle_index = physical_system.state_positions[self._angle] + self._limits = np.concatenate((physical_system.limits, [1., 1.])) + self._nominal_state = np.concatenate((physical_system.nominal_state, [1., 1.])) + self._state_names = physical_system.state_names + [f'cos({self._angle})', f'sin({self._angle})'] + self._state_positions = {key: index for index, key in enumerate(self._state_names)} + return self + + def reset(self): + # Docstring of super class + state = self._physical_system.reset() + return self._get_cos_sin(state) + + def simulate(self, action): + # Docstring of super class + state = self._physical_system.simulate(action) + return self._get_cos_sin(state) + + def _get_cos_sin(self, state): + """Appends the cosine and sine of the specified state to the state vector. + + Args: + state(numpy.ndarray[float]): The state vector of the system. + + Returns: + numpy.ndarray[float]: The state vector extended by cosine and sine. + """ + return np.concatenate((state, [np.cos(state[self._angle_index]), np.sin(state[self._angle_index])])) diff --git a/gym_electric_motor/state_action_processors/current_sum_processor.py b/gym_electric_motor/state_action_processors/current_sum_processor.py index 401bd86f..50bb7d60 100644 --- a/gym_electric_motor/state_action_processors/current_sum_processor.py +++ b/gym_electric_motor/state_action_processors/current_sum_processor.py @@ -5,8 +5,16 @@ class CurrentSumProcessor(StateActionProcessor): + """Adds an ``i_sum`` state to the systems state vector that adds up several currents.""" def __init__(self, currents, limit='max', physical_system=None): + """ + Args: + currents(Iterable[string]): Iterable of the names of the currents to be summed up. + limit('max'/'sum'): Selection if the limit and nominal values of ``i_sum`` are calculated by the maximum of + the limits / nominal values of the source currents or their summation. + physical_system(PhysicalSystem(optional)): The inner PhysicalSystem of this processor. + """ self._currents = currents assert limit in ['max', 'sum'] self._limit = max if limit == 'max' else np.sum @@ -14,26 +22,44 @@ def __init__(self, currents, limit='max', physical_system=None): super().__init__(physical_system) def set_physical_system(self, physical_system): + # Docstring of superclass super().set_physical_system(physical_system) + self._current_indices = [physical_system.state_positions[current] for current in self._currents] + + # Define the new state space as concatenation of the old state space and [-1,1] for i_sum low = np.concatenate((physical_system.state_space.low, [-1.])) high = np.concatenate((physical_system.state_space.high, [1.])) self.state_space = gym.spaces.Box(low, high, dtype=np.float64) - self._current_indices = [physical_system.state_positions[current] for current in self._currents] + + # Set the new limits / nominal values of the state vector current_limit = self._limit(physical_system.limits[self._current_indices]) current_nominal_value = self._limit(physical_system.nominal_state[self._current_indices]) self._limits = np.concatenate((physical_system.limits, [current_limit])) self._nominal_state = np.concatenate((physical_system.nominal_state, [current_nominal_value])) + + # Append the new state to the state name vector and the state positions dictionary self._state_names = physical_system.state_names + ['i_sum'] - self._state_positions = {key: index for index, key in enumerate(self._state_names)} + self._state_positions = physical_system.state_positions.copy() + self._state_positions['i_sum'] = self._state_names.index('i_sum') return self def reset(self): + # Docstring of superclass state = self._physical_system.reset() return np.concatenate((state, [self._get_current_sum(state)])) def simulate(self, action): + # Docstring of superclass state = self._physical_system.simulate(action) return np.concatenate((state, [self._get_current_sum(state)])) def _get_current_sum(self, state): + """Calculates the sum of the currents from the state + + Args: + state(numpy.ndarray[float]): The state of the inner system. + + Returns: + float: The summation of the currents of the state. + """ return np.sum(state[self._current_indices]) diff --git a/gym_electric_motor/state_action_processors/dead_time_processor.py b/gym_electric_motor/state_action_processors/dead_time_processor.py index 5ffdb4fb..c21f7651 100644 --- a/gym_electric_motor/state_action_processors/dead_time_processor.py +++ b/gym_electric_motor/state_action_processors/dead_time_processor.py @@ -6,41 +6,76 @@ class DeadTimeProcessor(StateActionProcessor): + """The DeadTimeProcessor delays the actions to the physical system for a parameterizable amount of steps. + + Reset Actions: + When the environment is reset, no valid previous actions are available. Per default, constant reset actions are + used. Also, custom reset actions parameterized. Therefore, a method can be passed during initialization of a + DeadTimeProcessor. This method needs to return a list of valid actions of the action space whose length equals + the number of dead time steps. + + The default reset actions are zeros. The concrete shape is derived by the concrete action spaces: + ``Discrete, MultiDiscrete, Box``. + """ @property def dead_time(self): - return True + """int: The number of delayed steps.""" + return self._steps def __init__(self, steps=1, reset_action=None, physical_system=None): - self._reset_action = reset_action + """Args: + steps(int): Number of steps to delay the actions. + reset_action(callable): A callable that returns a list of length steps to initialize the dead-actions + after a reset. Default: See above in the class description + physical_system(PhysicalSystem (optional)): The inner physical system of this StateActionProcessor. + """ + self._reset_actions = reset_action self._action_deque = deque(maxlen=steps) + self._steps = int(steps) super().__init__(physical_system) def set_physical_system(self, physical_system): + """Sets the inner PhysicalSystem of the DeadTimeProcessor. - if self._reset_action is None: + Args: + physical_system(PhysicalSystem): The physical system to be set. + """ + if self._reset_actions is None: action_space = physical_system.action_space if isinstance(action_space, gym.spaces.Discrete): - self._reset_action = 0 + reset_action = 0 elif isinstance(action_space, gym.spaces.MultiDiscrete): - self._reset_action = np.zeros_like(action_space.nvec) + reset_action = [np.zeros_like(action_space.nvec)] elif isinstance(action_space, gym.spaces.Box): - self._reset_action = np.zeros(action_space.shape, dtype=np.float64) + reset_action = np.zeros(action_space.shape, dtype=np.float64) else: raise AssertionError( f'Action Space {action_space} of type {type(action_space)} unsupported.' 'Only Discrete / MultiDiscrete and Box allowed for the dead time processor.' ) + self._reset_actions = lambda: [reset_action] * self._action_deque.maxlen def reset(self): + """Resets the processor and the inner physical system for a new episode. + + Returns: + numpy.ndarray[float]: The initial state of the system. + """ state = super().reset() self._action_deque.clear() - self._action_deque.extend([self._reset_action] * self._action_deque.maxlen) + self._action_deque.extend(self._reset_actions()) return state def simulate(self, action): + """Saves the action, applies the dead-time action and simulates the system for one time step. + + Args: + action(element of the action_space): The next action for the system. + + Returns: + numpy.ndarray[float]: The next state of the system. + """ active_action = self._action_deque.pop() self._action_deque.appendleft(action) return self._physical_system.simulate(active_action) - - diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py index 09bf7b64..5057cf2d 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -6,16 +6,32 @@ class DqToAbcActionProcessor(StateActionProcessor): - + """The DqToAbcActionProcessor converts an inner system with an AC motor and actions in abc coordinates to a + system to which actions in the dq-coordinate system can be applied. + """ @property def action_space(self): return gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) @staticmethod def _transformation(action, angle): + """Transforms the action in dq-space to an action in the abc-space for the use in the inner system. + + Args: + action(numpy.ndarray[float]): action in the dq-space + angle(float): Current angle of the system. + + Returns: + numpy.ndarray[float]: The action in the abc-space + """ return ps.ThreePhaseMotor.t_32(ps.ThreePhaseMotor.q_inv(action, angle)) def __init__(self, angle_name=None, physical_system=None): + """ + Args: + angle_name(string): Name of the state that defines the current angle of the AC-motor + physical_system(PhysicalSystem(optional)): The inner physical system. + """ self._angle = 0.0 self._angle_index = None self._omega_index = None @@ -25,35 +41,43 @@ def __init__(self, angle_name=None, physical_system=None): super().__init__(physical_system) def set_physical_system(self, physical_system): - assert isinstance(physical_system.unwrapped, ps.SCMLSystem) - assert isinstance(physical_system.electrical_motor, ps.ThreePhaseMotor) + # Docstring of super class + assert isinstance(physical_system.unwrapped, ps.SCMLSystem),\ + 'Only SCML Systems can be used within this processor' + assert isinstance(physical_system.electrical_motor, ps.ThreePhaseMotor), \ + 'The motor in the system has to derive from the ThreePhaseMotor to define transformations.' super().set_physical_system(physical_system) + + # If no angle name was passed, try to use defaults. ('epsilon' for sync motors 'psi_angle' for induction motors) if self._angle_name is None: if isinstance(physical_system.electrical_motor, (ps.PermanentMagnetSynchronousMotor, ps.SynchronousReluctanceMotor)): self._angle_name = 'epsilon' else: self._angle_name = 'psi_angle' + assert self._angle_name in physical_system.state_names, \ f'Angle {self._angle_name} not in the states of the physical system. Probably, a flux observer is required.' self._angle_index = physical_system.state_names.index(self._angle_name) self._omega_index = physical_system.state_names.index('omega') - if hasattr(physical_system.converter, 'dead_time') \ - and physical_system.converter.dead_time is True: - self._angle_advance = 1.5 - else: - self._angle_advance = 0.5 + self._angle_advance = 0.5 + # If dead time has been added to the system increase the angle advance by the amount of dead time steps + if hasattr(physical_system, 'dead_time'): + self._angle_advance += physical_system.dead_time return self def simulate(self, action): - advanced_angle = self._state[self._angle_index] * self._physical_system.limits[self._angle_index] \ - + self._angle_advance * self._physical_system.tau \ - * self._state[self._omega_index] * self._physical_system.limits[self._omega_index] + # Docstring of super class + advanced_angle = self._state[self._angle_index] \ + + self._angle_advance * self._physical_system.tau * self._state[self._omega_index] abc_action = self._transformation(action, advanced_angle) - self._state = self._physical_system.simulate(abc_action) - return self._state + normalized_state = self._physical_system.simulate(abc_action) + self._state = normalized_state * self._physical_system.limits + return normalized_state def reset(self, **kwargs): - self._state = self._physical_system.reset() - return self._state + # Docstring of super class + normalized_state = self._physical_system.reset() + self._state = normalized_state * self._physical_system.limits + return normalized_state diff --git a/gym_electric_motor/state_action_processors/flux_observer.py b/gym_electric_motor/state_action_processors/flux_observer.py index cf727171..7a180ee9 100644 --- a/gym_electric_motor/state_action_processors/flux_observer.py +++ b/gym_electric_motor/state_action_processors/flux_observer.py @@ -6,8 +6,28 @@ class FluxObserver(StateActionProcessor): + """The FluxObserver extends the systems state vector by estimated flux states ``psi_abs``, and ``psi_angle``. + + .. math:: psi_{abs} = |\Psi | + + .. math:: psi_{angle} = \\angle{\Psi_t} + + .. math:: \Psi \in \mathbb{C} + + .. math:: I_{s, \\alpha \\beta} = \\left( I_{s,\\alpha}, I_{s, \\beta} \\right) ^T + + .. math:: + \Delta \Psi_k = \\frac {(I_{s, \\alpha}+jI_{s, \\beta}) R_r L_m}{L_r} + - \Psi_{k-1}\\frac{R_r}{L_r}j\omega + + .. math :: + \Psi_k = \sum_{i=0}^k (\Psi_{k-1} + \Delta\Psi_k) \\tau + """ def __init__(self, current_names=('i_sa', 'i_sb', 'i_sc'), physical_system=None): + """ + Args: + """ super(FluxObserver, self).__init__(physical_system) self._current_indices = None self._l_m = None # Main induction @@ -61,9 +81,9 @@ def simulate(self, action): [i_s_alpha, i_s_beta] = self._abc_to_alphabeta_transformation(i_s) # Calculate delta flux - delta = np.complex(i_s_alpha, i_s_beta) * self._r_r * self._l_m / self._l_r \ + delta_psi = np.complex(i_s_alpha, i_s_beta) * self._r_r * self._l_m / self._l_r \ - self._integrated * np.complex(self._r_r / self._l_r, -omega) # Integrate the flux - self._integrated += delta * self._physical_system.tau + self._integrated += delta_psi * self._physical_system.tau return np.concatenate((state, [np.abs(self._integrated), np.angle(self._integrated)])) / self._limits diff --git a/gym_electric_motor/state_action_processors/state_action_processor.py b/gym_electric_motor/state_action_processors/state_action_processor.py index 60c6b3b6..1e6e67b4 100644 --- a/gym_electric_motor/state_action_processors/state_action_processor.py +++ b/gym_electric_motor/state_action_processors/state_action_processor.py @@ -5,7 +5,7 @@ class StateActionProcessor(gem.core.PhysicalSystem, gem.core.RandomComponent): """A StateActionProcessor is a wrapper around the PhysicalSystem of an gem-environment. It may be used to modify its states and actions. In contrast to gym-wrappers which are put around the whole - whole environment, modified states by the StateActionProcessors can be referenced, rewarded and visualized by the + environment, modified states by the StateActionProcessors can be referenced, rewarded and visualized by the other components of the environment. """ @@ -37,7 +37,7 @@ def state_space(self, space): @property def physical_system(self): - """The inner physical_system""" + """The inner physical_system.""" return self._physical_system @property From e647d82764cd7ab8b0eae8138951960f325152f8 Mon Sep 17 00:00:00 2001 From: Arne Date: Fri, 10 Dec 2021 16:21:50 +0100 Subject: [PATCH 10/79] Documentation fix --- gym_electric_motor/state_action_processors/flux_observer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym_electric_motor/state_action_processors/flux_observer.py b/gym_electric_motor/state_action_processors/flux_observer.py index 7a180ee9..e6db272a 100644 --- a/gym_electric_motor/state_action_processors/flux_observer.py +++ b/gym_electric_motor/state_action_processors/flux_observer.py @@ -18,7 +18,7 @@ class FluxObserver(StateActionProcessor): .. math:: \Delta \Psi_k = \\frac {(I_{s, \\alpha}+jI_{s, \\beta}) R_r L_m}{L_r} - - \Psi_{k-1}\\frac{R_r}{L_r}j\omega + - \Psi_{k-1}(\\frac{R_r}{L_r}+ j\omega) .. math :: \Psi_k = \sum_{i=0}^k (\Psi_{k-1} + \Delta\Psi_k) \\tau From 6af808d3547e378068307e8029af0876134b6008 Mon Sep 17 00:00:00 2001 From: Arne Date: Sun, 12 Dec 2021 17:14:04 +0100 Subject: [PATCH 11/79] Finished docs and started StateActionProcessor Tests --- .../cos_sin_processor.rst | 6 +++ .../current_sum_processor.rst | 4 +- .../dead_time_processor.rst | 6 +++ .../dq_to_abc_action_processor.rst | 7 +++ .../state_action_processors/flux_observer.rst | 2 +- .../state_action_processor.rst | 6 ++- gym_electric_motor/__init__.py | 2 + .../state_action_processors/__init__.py | 3 ++ .../cos_sin_processor.py | 5 +++ .../current_sum_processor.py | 2 +- .../state_action_processors/flux_observer.py | 7 +++ .../state_noise_processor.py | 40 +++++++++++++++++ .../test_state_action_processors/__init__.py | 0 .../test_cos_sin_processor.py | 38 ++++++++++++++++ .../test_state_action_processor.py | 45 +++++++++++++++++++ tests/utils/state_action_test_processor.py | 18 ++++++++ 16 files changed, 186 insertions(+), 5 deletions(-) create mode 100644 docs/parts/state_action_processors/cos_sin_processor.rst create mode 100644 docs/parts/state_action_processors/dead_time_processor.rst create mode 100644 docs/parts/state_action_processors/dq_to_abc_action_processor.rst create mode 100644 tests/test_state_action_processors/__init__.py create mode 100644 tests/test_state_action_processors/test_cos_sin_processor.py create mode 100644 tests/test_state_action_processors/test_state_action_processor.py create mode 100644 tests/utils/state_action_test_processor.py diff --git a/docs/parts/state_action_processors/cos_sin_processor.rst b/docs/parts/state_action_processors/cos_sin_processor.rst new file mode 100644 index 00000000..09dd01c8 --- /dev/null +++ b/docs/parts/state_action_processors/cos_sin_processor.rst @@ -0,0 +1,6 @@ +Cos Sin Processor +##################### + +.. autoclass:: gym_electric_motor.state_action_processors.CosSinProcessor + :members: + :inherited-members: diff --git a/docs/parts/state_action_processors/current_sum_processor.rst b/docs/parts/state_action_processors/current_sum_processor.rst index 9ffad1d6..107ac5ac 100644 --- a/docs/parts/state_action_processors/current_sum_processor.rst +++ b/docs/parts/state_action_processors/current_sum_processor.rst @@ -1,6 +1,6 @@ Current Sum Processor -##################### +###################### .. autoclass:: gym_electric_motor.state_action_processors.current_sum_processor.CurrentSumProcessor :members: - :inherited-members: \ No newline at end of file + :inherited-members: diff --git a/docs/parts/state_action_processors/dead_time_processor.rst b/docs/parts/state_action_processors/dead_time_processor.rst new file mode 100644 index 00000000..978d8533 --- /dev/null +++ b/docs/parts/state_action_processors/dead_time_processor.rst @@ -0,0 +1,6 @@ +Dead Time Processor +##################### + +.. autoclass:: gym_electric_motor.state_action_processors.DeadTimeProcessor + :members: + :inherited-members: diff --git a/docs/parts/state_action_processors/dq_to_abc_action_processor.rst b/docs/parts/state_action_processors/dq_to_abc_action_processor.rst new file mode 100644 index 00000000..5b37a066 --- /dev/null +++ b/docs/parts/state_action_processors/dq_to_abc_action_processor.rst @@ -0,0 +1,7 @@ +Dq To Abc Action Processor +########################## + +.. autoclass:: gym_electric_motor.state_action_processors.DqToAbcActionProcessor + :members: + :inherited-members: + diff --git a/docs/parts/state_action_processors/flux_observer.rst b/docs/parts/state_action_processors/flux_observer.rst index afddec37..3e5be2f4 100644 --- a/docs/parts/state_action_processors/flux_observer.rst +++ b/docs/parts/state_action_processors/flux_observer.rst @@ -1,5 +1,5 @@ FluxObserver -##################### +###################### .. autoclass:: gym_electric_motor.state_action_processors.FluxObserver :members: diff --git a/docs/parts/state_action_processors/state_action_processor.rst b/docs/parts/state_action_processors/state_action_processor.rst index 18be1f73..75f25746 100644 --- a/docs/parts/state_action_processors/state_action_processor.rst +++ b/docs/parts/state_action_processors/state_action_processor.rst @@ -7,9 +7,13 @@ State Action Processor current_sum_processor flux_observer + cos_sin_processor + dead_time_processor + dq_to_abc_action_processor + State Action Processor Base Class '''''''''''''''''''''''''''''''''' .. autoclass:: gym_electric_motor.state_action_processors.StateActionProcessor - :members: \ No newline at end of file + :members: diff --git a/gym_electric_motor/__init__.py b/gym_electric_motor/__init__.py index dc150a1d..c5f0ff44 100644 --- a/gym_electric_motor/__init__.py +++ b/gym_electric_motor/__init__.py @@ -17,6 +17,8 @@ import gym_electric_motor.visualization import gym_electric_motor.physical_systems import gym_electric_motor.envs +import gym_electric_motor.state_action_processors + from gym.envs.registration import register import gym from packaging import version diff --git a/gym_electric_motor/state_action_processors/__init__.py b/gym_electric_motor/state_action_processors/__init__.py index c2675ba4..95160342 100644 --- a/gym_electric_motor/state_action_processors/__init__.py +++ b/gym_electric_motor/state_action_processors/__init__.py @@ -2,3 +2,6 @@ from .current_sum_processor import CurrentSumProcessor from .flux_observer import FluxObserver from .dq_to_abc_action_processor import DqToAbcActionProcessor +from .cos_sin_processor import CosSinProcessor +from .state_noise_processor import StateNoiseProcessor +from .dead_time_processor import DeadTimeProcessor diff --git a/gym_electric_motor/state_action_processors/cos_sin_processor.py b/gym_electric_motor/state_action_processors/cos_sin_processor.py index bf0f8c72..2ad630aa 100644 --- a/gym_electric_motor/state_action_processors/cos_sin_processor.py +++ b/gym_electric_motor/state_action_processors/cos_sin_processor.py @@ -9,6 +9,11 @@ class CosSinProcessor(StateActionProcessor): certain systems state. """ + @property + def angle(self): + """Returns the name of the state whose cosine and sine are appended to the state vector.""" + return self._angle + def __init__(self, angle='epsilon', physical_system=None): """ Args: diff --git a/gym_electric_motor/state_action_processors/current_sum_processor.py b/gym_electric_motor/state_action_processors/current_sum_processor.py index 50bb7d60..aaa7cb11 100644 --- a/gym_electric_motor/state_action_processors/current_sum_processor.py +++ b/gym_electric_motor/state_action_processors/current_sum_processor.py @@ -5,7 +5,7 @@ class CurrentSumProcessor(StateActionProcessor): - """Adds an ``i_sum`` state to the systems state vector that adds up several currents.""" + """Adds an ``i_sum`` state to the systems state vector that adds up currents.""" def __init__(self, currents, limit='max', physical_system=None): """ diff --git a/gym_electric_motor/state_action_processors/flux_observer.py b/gym_electric_motor/state_action_processors/flux_observer.py index e6db272a..22cec368 100644 --- a/gym_electric_motor/state_action_processors/flux_observer.py +++ b/gym_electric_motor/state_action_processors/flux_observer.py @@ -27,6 +27,10 @@ class FluxObserver(StateActionProcessor): def __init__(self, current_names=('i_sa', 'i_sb', 'i_sc'), physical_system=None): """ Args: + current_names(Iterable[string]): Names of the currents to be observed to estimate the flux. + (Default: ``('i_sa', 'i_sb', 'i_sc')``) + physical_system(PhysicalSystem): (Optional) Physical System to initialize this observer. If not passed, + the observer will be initialized during environment creation. """ super(FluxObserver, self).__init__(physical_system) self._current_indices = None @@ -46,6 +50,7 @@ def _abc_to_alphabeta_transformation(i_s): return gem.physical_systems.electric_motors.ThreePhaseMotor.t_23(i_s) def set_physical_system(self, physical_system): + # Docstring of super class assert isinstance(physical_system.electrical_motor, gem.physical_systems.electric_motors.InductionMotor) super().set_physical_system(physical_system) low = np.concatenate((physical_system.state_space.low, [-1., -1.])) @@ -68,10 +73,12 @@ def set_physical_system(self, physical_system): return self def reset(self): + # Docstring of super class self._integrated = np.complex(0, 0) return np.concatenate((super().reset(), [0.0, 0.0])) def simulate(self, action): + # Docstring of super class state_norm = self._physical_system.simulate(action) state = state_norm * self._physical_system.limits i_s = state[self._i_s_idx] diff --git a/gym_electric_motor/state_action_processors/state_noise_processor.py b/gym_electric_motor/state_action_processors/state_noise_processor.py index 41d91f0d..545f904b 100644 --- a/gym_electric_motor/state_action_processors/state_noise_processor.py +++ b/gym_electric_motor/state_action_processors/state_noise_processor.py @@ -2,9 +2,27 @@ class StateNoiseProcessor(StateActionProcessor): + """The StateNoiseProcessor puts additional noise onto the systems state. + + The random distribution of the noise can be selected by those available in the numpy random generator: + ``_ + + Example: + .. code-block:: python + import gym_electric_motor as gem + state_noise_processor = gem.state_action_processors.StateNoiseProcessor( + states=['omega', 'torque'], + random_dist='laplace' + random_kwargs=dict(loc=0.0, scale=0.1) + ) + env = gem.make('Cont-SC-PermExDc-v0', state_action_processors=(state_noise_processor,)) + + + """ @property def random_kwargs(self): + """Returns the random keyword arguments that are passed through to the random generator function.""" return self._random_kwargs @random_kwargs.setter @@ -12,6 +30,18 @@ def random_kwargs(self, value): self._random_kwargs = dict(value) def __init__(self, states, random_dist='normal', random_kwargs=(), random_length=1000, physical_system=None): + """ + Args: + states(Iterable[string] / 'all'): Names of the states onto which the noise shall be added. + Shortcut 'all': Add it to all available states. + random_dist(string): Name of the random distribution behind the numpy random generator that shall be used. + random_kwargs(dict): Keyword arguments that are passed through to the selected random distribution. + For available entries have a look at the page linked above. Only the argument 'size' cannot be passed. + random_length(int): Number of random samples that are drawn at once. Drawing from the random distribution + at every cycle would be too slow. So, multiple samples are drawn at once for the next steps. + physical_system(PhysicalSystem): (Optional) If available the inner physical system can already be passed to + the processor here. Otherwise the processor will be initialized during environment creation. + """ self._random_kwargs = dict(random_kwargs) self._random_length = int(random_length) self._random_pointer = 0 @@ -25,26 +55,36 @@ def __init__(self, states, random_dist='normal', random_kwargs=(), random_length 'Check https://numpy.org/doc/stable/reference/random/generator.html#distributions for distributions.' def set_physical_system(self, physical_system): + # Docstring from super class super().set_physical_system(physical_system) self._state_indices = [physical_system.state_positions[state_name] for state_name in self._states] return self def reset(self): + # Docstring from super class state = super().reset() self._new_noise() return self._add_noise(state) def simulate(self, action): + # Docstring from super class if self._random_pointer >= self._random_length: self._new_noise() return self._add_noise(self._physical_system.simulate(action)) def _add_noise(self, state): + """Adds noise to the current state. + Args: + state(numpy.ndarray[float]): The systems state without noise. + Returns: + numpy.ndarray[float]): The state with additional noise. + """ state[self._state_indices] = state[self._state_indices] + self._noise[self._random_pointer] self._random_pointer += 1 return state def _new_noise(self): + """Samples new noise from the random distribution for the next steps.""" self._random_pointer = 0 fct = getattr(self._random_generator, self._random_dist) self._noise = fct(size=(self._random_length, len(self._state)), **self._random_kwargs) diff --git a/tests/test_state_action_processors/__init__.py b/tests/test_state_action_processors/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_state_action_processors/test_cos_sin_processor.py b/tests/test_state_action_processors/test_cos_sin_processor.py new file mode 100644 index 00000000..eb6da0fc --- /dev/null +++ b/tests/test_state_action_processors/test_cos_sin_processor.py @@ -0,0 +1,38 @@ +import gym +import pytest +import numpy as np +import gym_electric_motor as gem + +from ..testing_utils import DummyPhysicalSystem +from .test_state_action_processor import TestStateActionProcessor + + +class TestCosSinProcessor(TestStateActionProcessor): + + @pytest.fixture + def processor(self, physical_system): + return gem.state_action_processors.CosSinProcessor(angle='dummy_state_0', physical_system=physical_system) + + def test_limits(self, processor, physical_system): + assert all(processor.limits == np.concatenate((physical_system.limits, [1., 1.]))) + + def test_nominal_state(self, processor, physical_system): + assert all(processor.nominal_state == np.concatenate((physical_system.nominal_state, [1., 1.]))) + + def test_state_space(self, processor, physical_system): + low = np.concatenate((physical_system.state_space.low, [-1, -1])) + high = np.concatenate((physical_system.state_space.high, [1, 1])) + space = gym.spaces.Box(low, high) + assert processor.state_space == space + + def test_reset(self, processor, physical_system): + assert all(processor.reset() == np.concatenate((physical_system.state, [1., 0.]))) + + @pytest.mark.parametrize('action', [1, 2, 3, 4]) + def test_simulate(self, processor, physical_system, action): + state = processor.simulate(action) + ps_state = physical_system.state + assert action == physical_system.action + cos_sin_state = ps_state[physical_system.state_positions[processor.angle]] + assert all(state == np.concatenate((ps_state, [np.cos(cos_sin_state), np.sin(cos_sin_state)]))) + diff --git a/tests/test_state_action_processors/test_state_action_processor.py b/tests/test_state_action_processors/test_state_action_processor.py new file mode 100644 index 00000000..d9124f9d --- /dev/null +++ b/tests/test_state_action_processors/test_state_action_processor.py @@ -0,0 +1,45 @@ +import pytest +import gym_electric_motor as gem +from ..testing_utils import DummyPhysicalSystem + + +class TestStateActionProcessor: + + @pytest.fixture + def physical_system(self): + return DummyPhysicalSystem() + + @pytest.fixture + def processor(self, physical_system): + return gem.state_action_processors.StateActionProcessor(physical_system=physical_system) + + @pytest.fixture + def double_wrapped(self, processor): + return gem.state_action_processors.StateActionProcessor(physical_system=processor) + + def test_action_space(self, processor, physical_system): + assert processor.action_space == physical_system.action_space + + def test_state_space(self, processor, physical_system): + assert processor.state_space == physical_system.state_space + + def test_physical_system(self, processor, physical_system): + assert processor.physical_system == physical_system + + def test_unwrapped(self, double_wrapped, physical_system): + assert double_wrapped.unwrapped == physical_system + + def test_nominal_state(self, processor, physical_system): + assert processor.nominal_state == physical_system.nominal_state + + def test_limits(self, processor, physical_system): + assert processor.limits == physical_system.limits + + def test_reset(self, processor, physical_system): + assert processor.reset() == physical_system.state + + @pytest.mark.parametrize(['action'], [[1]]) + def test_simulate(self, processor, physical_system, action): + state = processor.simulate(action) + assert state == physical_system.state + assert action == physical_system.action diff --git a/tests/utils/state_action_test_processor.py b/tests/utils/state_action_test_processor.py new file mode 100644 index 00000000..45c4ed44 --- /dev/null +++ b/tests/utils/state_action_test_processor.py @@ -0,0 +1,18 @@ +import gym_electric_motor as gem + + +class StateActionTestProcessor(gem.state_action_processors.StateActionProcessor): + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.action = None + self.state = None + + def simulate(self, action): + self.action = action + self.state = self.physical_system.simulate(action) + return self.state + + def reset(self): + self.state = self.physical_system.reset() + return self.state From 9a6f9dfa1186179cd1edb6b76c5af0105d251f13 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 13 Dec 2021 22:03:29 +0100 Subject: [PATCH 12/79] intermediate save --- .../test_flux_observer.py | 43 +++++++++++++++++++ tests/utils/__init__.py | 1 + 2 files changed, 44 insertions(+) create mode 100644 tests/test_state_action_processors/test_flux_observer.py create mode 100644 tests/utils/__init__.py diff --git a/tests/test_state_action_processors/test_flux_observer.py b/tests/test_state_action_processors/test_flux_observer.py new file mode 100644 index 00000000..e58ca717 --- /dev/null +++ b/tests/test_state_action_processors/test_flux_observer.py @@ -0,0 +1,43 @@ +import gym +import pytest +import numpy as np +import gym_electric_motor as gem + +from ..testing_utils import DummyPhysicalSystem +from ..utils import StateActionTestProcessor +from .test_state_action_processor import TestStateActionProcessor + + +class TestFluxObserver(TestStateActionProcessor): + + @pytest.fixture + def physical_system(self): + return StateActionTestProcessor(physical_system=gem.physical_systems.SquirrelCageInductionMotorSystem()) + + @pytest.fixture + def processor(self, physical_system): + return gem.state_action_processors.FluxObserver(physical_system=physical_system) + + def test_limits(self, processor, physical_system): + assert all(processor.limits == np.concatenate((physical_system.limits, [1., 1.]))) + + def test_nominal_state(self, processor, physical_system): + assert all(processor.nominal_state == np.concatenate((physical_system.nominal_state, [1., 1.]))) + + def test_state_space(self, processor, physical_system): + low = np.concatenate((physical_system.state_space.low, [-1, -1])) + high = np.concatenate((physical_system.state_space.high, [1, 1])) + space = gym.spaces.Box(low, high) + assert processor.state_space == space + + def test_reset(self, processor, physical_system): + assert all(processor.reset() == np.concatenate((physical_system.state, [1., 0.]))) + + @pytest.mark.parametrize('action', [1, 2, 3, 4]) + def test_simulate(self, processor, physical_system, action): + state = processor.simulate(action) + ps_state = physical_system.state + assert action == physical_system.action + cos_sin_state = ps_state[physical_system.state_positions[processor.angle]] + assert all(state == np.concatenate((ps_state, [np.cos(cos_sin_state), np.sin(cos_sin_state)]))) + diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py new file mode 100644 index 00000000..511e55e5 --- /dev/null +++ b/tests/utils/__init__.py @@ -0,0 +1 @@ +from .state_action_test_processor import StateActionTestProcessor From f91535cf280bcb1ca0c6563c42c83615c12ec522 Mon Sep 17 00:00:00 2001 From: Arne Date: Sun, 19 Dec 2021 11:13:50 +0100 Subject: [PATCH 13/79] Tests for state action processors --- .../dead_time_processor.py | 4 +- .../dq_to_abc_action_processor.py | 2 - .../state_action_processors/flux_observer.py | 19 ++--- .../test_cos_sin_processor.py | 2 +- .../test_dead_time_processor.py | 77 +++++++++++++++++++ .../test_flux_observer.py | 44 +++++++---- .../test_state_action_processor.py | 22 ++++-- tests/testing_utils.py | 12 ++- 8 files changed, 143 insertions(+), 39 deletions(-) create mode 100644 tests/test_state_action_processors/test_dead_time_processor.py diff --git a/gym_electric_motor/state_action_processors/dead_time_processor.py b/gym_electric_motor/state_action_processors/dead_time_processor.py index c21f7651..a9d3feca 100644 --- a/gym_electric_motor/state_action_processors/dead_time_processor.py +++ b/gym_electric_motor/state_action_processors/dead_time_processor.py @@ -31,8 +31,9 @@ def __init__(self, steps=1, reset_action=None, physical_system=None): physical_system(PhysicalSystem (optional)): The inner physical system of this StateActionProcessor. """ self._reset_actions = reset_action - self._action_deque = deque(maxlen=steps) self._steps = int(steps) + assert self._steps > 0, f'The number of steps has to be greater than 0. A "{steps}" has been passed.' + self._action_deque = deque(maxlen=steps) super().__init__(physical_system) def set_physical_system(self, physical_system): @@ -41,6 +42,7 @@ def set_physical_system(self, physical_system): Args: physical_system(PhysicalSystem): The physical system to be set. """ + super().set_physical_system(physical_system) if self._reset_actions is None: action_space = physical_system.action_space if isinstance(action_space, gym.spaces.Discrete): diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py index 5057cf2d..5846c650 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -42,8 +42,6 @@ def __init__(self, angle_name=None, physical_system=None): def set_physical_system(self, physical_system): # Docstring of super class - assert isinstance(physical_system.unwrapped, ps.SCMLSystem),\ - 'Only SCML Systems can be used within this processor' assert isinstance(physical_system.electrical_motor, ps.ThreePhaseMotor), \ 'The motor in the system has to derive from the ThreePhaseMotor to define transformations.' super().set_physical_system(physical_system) diff --git a/gym_electric_motor/state_action_processors/flux_observer.py b/gym_electric_motor/state_action_processors/flux_observer.py index 22cec368..62306229 100644 --- a/gym_electric_motor/state_action_processors/flux_observer.py +++ b/gym_electric_motor/state_action_processors/flux_observer.py @@ -32,7 +32,6 @@ def __init__(self, current_names=('i_sa', 'i_sb', 'i_sc'), physical_system=None) physical_system(PhysicalSystem): (Optional) Physical System to initialize this observer. If not passed, the observer will be initialized during environment creation. """ - super(FluxObserver, self).__init__(physical_system) self._current_indices = None self._l_m = None # Main induction self._l_r = None # Induction of the rotor @@ -44,6 +43,8 @@ def __init__(self, current_names=('i_sa', 'i_sb', 'i_sc'), physical_system=None) # Integrated values of the flux for the two directions (Re: alpha, Im: beta) self._integrated = np.complex(0, 0) self._current_names = current_names + super(FluxObserver, self).__init__(physical_system) + @staticmethod def _abc_to_alphabeta_transformation(i_s): @@ -53,20 +54,20 @@ def set_physical_system(self, physical_system): # Docstring of super class assert isinstance(physical_system.electrical_motor, gem.physical_systems.electric_motors.InductionMotor) super().set_physical_system(physical_system) - low = np.concatenate((physical_system.state_space.low, [-1., -1.])) - high = np.concatenate((physical_system.state_space.high, [1., 1.])) + mp = physical_system.electrical_motor.motor_parameter + self._l_m = mp['l_m'] # Main induction + self._l_r = mp['l_m'] + mp['l_sigr'] # Induction of the rotor + self._r_r = mp['r_r'] # Rotor resistance + self._p = mp['p'] # Pole pair number + psi_limit = self._l_m * physical_system.limits[physical_system.state_names.index('i_sd')] + low = np.concatenate((physical_system.state_space.low, [-psi_limit, -np.pi])) + high = np.concatenate((physical_system.state_space.high, [psi_limit, np.pi])) self.state_space = gym.spaces.Box(low, high, dtype=np.float64) self._current_indices = [physical_system.state_positions[name] for name in self._current_names] - psi_limit = 1.0 self._limits = np.concatenate((physical_system.limits, [psi_limit, np.pi])) self._nominal_state = np.concatenate((physical_system.nominal_state, [psi_limit, np.pi])) self._state_names = physical_system.state_names + ['psi_abs', 'psi_angle'] self._state_positions = {key: index for index, key in enumerate(self._state_names)} - mp = physical_system.electrical_motor.motor_parameter - self._l_m = mp['l_m'] # Main induction - self._l_r = mp['l_m'] + mp['l_sigr'] # Induction of the rotor - self._r_r = mp['r_r'] # Rotor resistance - self._p = mp['p'] # Pole pair number self._i_s_idx = [physical_system.state_positions[name] for name in self._current_names] self._omega_idx = physical_system.state_positions['omega'] diff --git a/tests/test_state_action_processors/test_cos_sin_processor.py b/tests/test_state_action_processors/test_cos_sin_processor.py index eb6da0fc..e1c80286 100644 --- a/tests/test_state_action_processors/test_cos_sin_processor.py +++ b/tests/test_state_action_processors/test_cos_sin_processor.py @@ -3,7 +3,7 @@ import numpy as np import gym_electric_motor as gem -from ..testing_utils import DummyPhysicalSystem +from tests.testing_utils import DummyPhysicalSystem from .test_state_action_processor import TestStateActionProcessor diff --git a/tests/test_state_action_processors/test_dead_time_processor.py b/tests/test_state_action_processors/test_dead_time_processor.py new file mode 100644 index 00000000..0b22049a --- /dev/null +++ b/tests/test_state_action_processors/test_dead_time_processor.py @@ -0,0 +1,77 @@ +import gym +import pytest +import numpy as np +import gym_electric_motor as gem + +from .test_state_action_processor import TestStateActionProcessor +from tests.testing_utils import DummyPhysicalSystem + + +class TestDeadTimeProcessor(TestStateActionProcessor): + + @pytest.fixture + def processor(self, physical_system): + return gem.state_action_processors.DeadTimeProcessor(physical_system=physical_system) + + @pytest.fixture + def unset_processor(self, physical_system): + return gem.state_action_processors.DeadTimeProcessor() + + @pytest.mark.parametrize('action', [np.array([5.0]), np.array([2.0])]) + def test_simulate(self, reset_processor, physical_system, action): + state = reset_processor.simulate(action) + assert state == physical_system.state + assert all(physical_system.action == np.array([0.0])) + + @pytest.mark.parametrize('unset_processor', [ + gem.state_action_processors.DeadTimeProcessor(steps=2), + gem.state_action_processors.DeadTimeProcessor(steps=1), + gem.state_action_processors.DeadTimeProcessor(steps=5), + ]) + @pytest.mark.parametrize( + ['action_space', 'actions', 'reset_action'], + [ + [ + gym.spaces.Box(-100, 100, shape=(3,)), + [np.array([1., 2., 3.]), np.array([0., 1., 2.]), np.array([-1, 2, 3])], + np.array([0., 0., 0.]) + ], + [ + gym.spaces.Box(-100, 100, shape=(1,)), + [np.array([-1.]), np.array([0.]), np.array([-5.]), np.array([-6.]), np.array([-7.])], + np.array([0.]) + ], + [ + gym.spaces.MultiDiscrete([10, 20, 30]), + [[5, 8, 7], [3, 4, 5], [0, 0, 1], [0, 1, 1], [0, 0, 1]], + [0, 0, 0] + ], + [ + gym.spaces.Discrete(12), + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + 0 + ] + ] + ) + def test_execution(self, unset_processor, physical_system, action_space, actions, reset_action): + expected_actions = [reset_action] * unset_processor.dead_time + actions + physical_system._action_space = action_space + unset_processor.set_physical_system(physical_system) + unset_processor.reset() + for i, action in enumerate(actions): + unset_processor.simulate(action) + try: + assert physical_system.action == expected_actions[i] + except ValueError: + assert all(physical_system.action == expected_actions[i]) + + @pytest.mark.parametrize('processor', [gem.state_action_processors.DeadTimeProcessor()]) + def test_false_action_space(self, processor, physical_system): + physical_system._action_space = gym.spaces.MultiBinary(5) + with pytest.raises(AssertionError): + assert processor.set_physical_system(physical_system) + + @pytest.mark.parametrize('steps', [0, -10]) + def test_false_steps(self, steps): + with pytest.raises(AssertionError): + assert gem.state_action_processors.DeadTimeProcessor(steps) diff --git a/tests/test_state_action_processors/test_flux_observer.py b/tests/test_state_action_processors/test_flux_observer.py index e58ca717..22581229 100644 --- a/tests/test_state_action_processors/test_flux_observer.py +++ b/tests/test_state_action_processors/test_flux_observer.py @@ -3,8 +3,8 @@ import numpy as np import gym_electric_motor as gem -from ..testing_utils import DummyPhysicalSystem -from ..utils import StateActionTestProcessor +from tests.testing_utils import DummyPhysicalSystem +from tests.utils import StateActionTestProcessor from .test_state_action_processor import TestStateActionProcessor @@ -12,32 +12,46 @@ class TestFluxObserver(TestStateActionProcessor): @pytest.fixture def physical_system(self): - return StateActionTestProcessor(physical_system=gem.physical_systems.SquirrelCageInductionMotorSystem()) + ps = DummyPhysicalSystem(state_names=['omega','i_sa', 'i_sb', 'i_sc','i_sd', 'i_sq']) + ps.unwrapped.electrical_motor = gem.physical_systems.electric_motors.SquirrelCageInductionMotor( + limit_values=dict(i=20.0), + motor_parameter=dict(l_m=10.0) + ) + ps.unwrapped._limits[ps.state_names.index('i_sd')] = ps.unwrapped.electrical_motor.limits['i_sd'] + return ps + + @pytest.fixture + def reset_physical_system(self, physical_system): + ps.reset() + return ps @pytest.fixture def processor(self, physical_system): return gem.state_action_processors.FluxObserver(physical_system=physical_system) + @pytest.fixture + def reset_processor(self, processor): + processor.reset() + return processor + def test_limits(self, processor, physical_system): - assert all(processor.limits == np.concatenate((physical_system.limits, [1., 1.]))) + assert all(processor.limits == np.concatenate((physical_system.limits, [200., np.pi]))) def test_nominal_state(self, processor, physical_system): - assert all(processor.nominal_state == np.concatenate((physical_system.nominal_state, [1., 1.]))) + assert all(processor.nominal_state == np.concatenate((physical_system.nominal_state, [200., np.pi]))) def test_state_space(self, processor, physical_system): - low = np.concatenate((physical_system.state_space.low, [-1, -1])) - high = np.concatenate((physical_system.state_space.high, [1, 1])) - space = gym.spaces.Box(low, high) + psiabs_max = 200.0 + low = np.concatenate((physical_system.state_space.low, [-psiabs_max, -np.pi])) + high = np.concatenate((physical_system.state_space.high, [psiabs_max, np.pi])) + space = gym.spaces.Box(low, high, dtype=np.float64) assert processor.state_space == space def test_reset(self, processor, physical_system): - assert all(processor.reset() == np.concatenate((physical_system.state, [1., 0.]))) + assert all(processor.reset() == np.concatenate((physical_system.state, [0., 0.]))) @pytest.mark.parametrize('action', [1, 2, 3, 4]) - def test_simulate(self, processor, physical_system, action): - state = processor.simulate(action) - ps_state = physical_system.state - assert action == physical_system.action - cos_sin_state = ps_state[physical_system.state_positions[processor.angle]] - assert all(state == np.concatenate((ps_state, [np.cos(cos_sin_state), np.sin(cos_sin_state)]))) + def test_simulate(self, reset_processor, physical_system, action): + state = reset_processor.simulate(action) + assert all(state[:-2] == physical_system.state) diff --git a/tests/test_state_action_processors/test_state_action_processor.py b/tests/test_state_action_processors/test_state_action_processor.py index d9124f9d..82516d92 100644 --- a/tests/test_state_action_processors/test_state_action_processor.py +++ b/tests/test_state_action_processors/test_state_action_processor.py @@ -1,18 +1,24 @@ import pytest -import gym_electric_motor as gem -from ..testing_utils import DummyPhysicalSystem +from fontTools.ttx import process +import gym_electric_motor as gem +import tests.testing_utils as tu class TestStateActionProcessor: @pytest.fixture def physical_system(self): - return DummyPhysicalSystem() + return tu.DummyPhysicalSystem() @pytest.fixture def processor(self, physical_system): return gem.state_action_processors.StateActionProcessor(physical_system=physical_system) + @pytest.fixture + def reset_processor(self, processor): + processor.reset() + return processor + @pytest.fixture def double_wrapped(self, processor): return gem.state_action_processors.StateActionProcessor(physical_system=processor) @@ -30,16 +36,16 @@ def test_unwrapped(self, double_wrapped, physical_system): assert double_wrapped.unwrapped == physical_system def test_nominal_state(self, processor, physical_system): - assert processor.nominal_state == physical_system.nominal_state + assert all(processor.nominal_state == physical_system.nominal_state) def test_limits(self, processor, physical_system): - assert processor.limits == physical_system.limits + assert all(processor.limits == physical_system.limits) def test_reset(self, processor, physical_system): - assert processor.reset() == physical_system.state + assert all(processor.reset() == physical_system.state) @pytest.mark.parametrize(['action'], [[1]]) - def test_simulate(self, processor, physical_system, action): - state = processor.simulate(action) + def test_simulate(self, reset_processor, physical_system, action): + state = reset_processor.simulate(action) assert state == physical_system.state assert action == physical_system.action diff --git a/tests/testing_utils.py b/tests/testing_utils.py index 3908b029..7280a267 100644 --- a/tests/testing_utils.py +++ b/tests/testing_utils.py @@ -1,4 +1,4 @@ -from .conf import * +from tests.conf import * from gym_electric_motor.physical_systems import * from gym_electric_motor.utils import make_module, set_state_array from gym_electric_motor import ReferenceGenerator, RewardFunction, PhysicalSystem, ElectricMotorVisualization, \ @@ -244,10 +244,16 @@ def nominal_state(self): """ return self._nominal_values - def __init__(self, state_length=1, state_names='dummy_state', **kwargs): + def __init__(self, state_length=None, state_names='dummy_state', **kwargs): + + if isinstance(state_names, str): + if state_length is None: + state_length = 1 + state_names = [f'{state_names}_{i}' for i in range(state_length)] + state_length = len(state_names) super().__init__( Box(-1, 1, shape=(1,), dtype=np.float64), Box(-1, 1, shape=(state_length,), dtype=np.float64), - [f'{state_names}_{i}' for i in range(state_length)], 1 + state_names, 1 ) self._limits = np.array([10 * (i + 1) for i in range(state_length)]) self._nominal_values = np.array([(i + 1) for i in range(state_length)]) From e6c30132d00f290685d5db96beb90ff522fb47ac Mon Sep 17 00:00:00 2001 From: Arne Date: Sun, 9 Jan 2022 19:20:39 +0100 Subject: [PATCH 14/79] Tests for state action processors --- .../state_action_processors/flux_observer.rst | 2 +- .../state_action_processor.rst | 1 + .../state_noise_processor.rst | 6 +++ .../cos_sin_processor.py | 2 +- .../state_action_processor.py | 27 +++++++++++- .../state_noise_processor.py | 2 +- .../test_dq_to_abc_action_processor.py | 43 +++++++++++++++++++ .../test_flux_observer.py | 6 +-- 8 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 docs/parts/state_action_processors/state_noise_processor.rst create mode 100644 tests/test_state_action_processors/test_dq_to_abc_action_processor.py diff --git a/docs/parts/state_action_processors/flux_observer.rst b/docs/parts/state_action_processors/flux_observer.rst index 3e5be2f4..0562af07 100644 --- a/docs/parts/state_action_processors/flux_observer.rst +++ b/docs/parts/state_action_processors/flux_observer.rst @@ -1,4 +1,4 @@ -FluxObserver +Flux Observer ###################### .. autoclass:: gym_electric_motor.state_action_processors.FluxObserver diff --git a/docs/parts/state_action_processors/state_action_processor.rst b/docs/parts/state_action_processors/state_action_processor.rst index 75f25746..123718c9 100644 --- a/docs/parts/state_action_processors/state_action_processor.rst +++ b/docs/parts/state_action_processors/state_action_processor.rst @@ -10,6 +10,7 @@ State Action Processor cos_sin_processor dead_time_processor dq_to_abc_action_processor + state_noise_processor State Action Processor Base Class diff --git a/docs/parts/state_action_processors/state_noise_processor.rst b/docs/parts/state_action_processors/state_noise_processor.rst new file mode 100644 index 00000000..ed38a541 --- /dev/null +++ b/docs/parts/state_action_processors/state_noise_processor.rst @@ -0,0 +1,6 @@ +State Noise Processor +###################### + +.. autoclass:: gym_electric_motor.state_action_processors.StateNoiseProcessor + :members: + :inherited-members: diff --git a/gym_electric_motor/state_action_processors/cos_sin_processor.py b/gym_electric_motor/state_action_processors/cos_sin_processor.py index 2ad630aa..01e27111 100644 --- a/gym_electric_motor/state_action_processors/cos_sin_processor.py +++ b/gym_electric_motor/state_action_processors/cos_sin_processor.py @@ -19,7 +19,7 @@ def __init__(self, angle='epsilon', physical_system=None): Args: angle(string): Name of the state whose cosine and sine will be added to the systems state vector. Default: ``'epsilon'`` - physical_system(PhysicalSystem(optional): Inner system of this processor. + physical_system(PhysicalSystem(optional)): Inner system of this processor. """ self._angle = angle self._angle_index = None diff --git a/gym_electric_motor/state_action_processors/state_action_processor.py b/gym_electric_motor/state_action_processors/state_action_processor.py index 1e6e67b4..8d44d524 100644 --- a/gym_electric_motor/state_action_processors/state_action_processor.py +++ b/gym_electric_motor/state_action_processors/state_action_processor.py @@ -2,7 +2,7 @@ class StateActionProcessor(gem.core.PhysicalSystem, gem.core.RandomComponent): - """A StateActionProcessor is a wrapper around the PhysicalSystem of an gem-environment. + """A StateActionProcessor is a wrapper around the PhysicalSystem of a gem-environment. It may be used to modify its states and actions. In contrast to gym-wrappers which are put around the whole environment, modified states by the StateActionProcessors can be referenced, rewarded and visualized by the @@ -37,32 +37,47 @@ def state_space(self, space): @property def physical_system(self): - """The inner physical_system.""" + """The next inner physical_system or the next inner state_action_processor.""" return self._physical_system @property def limits(self): + """The processed physical system limits. + + If it is unset, the inner limits are returned.""" if self._limits is None: return self._physical_system.limits return self._limits @property def unwrapped(self): + """The innermost physical system within all state-action processors.""" return self.physical_system.unwrapped @property def nominal_state(self): + """The processed physical system nominal state. + + If it is unset, the inner nominal state is returned.""" if self._nominal_state is None: return self._physical_system.nominal_state return self._nominal_state @property def state_names(self): + """The processed physical system state names. + + If it is unset, the state names of the inner physical system are returned.""" if self._state_names is None: return self._physical_system.state_names return self._state_names def __init__(self, physical_system=None): + """ + Args: + physical_system(PhysicalSystem): (Optional) The inner physical system can be passed to directly call the + set_physical_system-method after the initialization. + """ gem.core.PhysicalSystem.__init__(self, None, None, (), None) gem.core.RandomComponent.__init__(self) self._physical_system = physical_system @@ -72,6 +87,11 @@ def __init__(self, physical_system=None): self.set_physical_system(physical_system) def set_physical_system(self, physical_system): + """Sets the inner physical system of this StateActionProcessor. + + Args: + physical_system(PhysicalSystem): The inner physical system or state action processor. + """ self._physical_system = physical_system self._action_space = physical_system.action_space self._state_names = physical_system.state_names @@ -80,6 +100,7 @@ def set_physical_system(self, physical_system): return self def seed(self, seed=None): + # docstring of super class RandomComponent if isinstance(self._physical_system, gem.core.RandomComponent): self._physical_system.seed(seed) @@ -87,9 +108,11 @@ def __getattr__(self, name): return getattr(self._physical_system, name) def simulate(self, action): + # Docstring of super class PhysicalSystem return self._physical_system.simulate(action) def reset(self, **kwargs): + # Docstring of super class PhysicalSystem self.next_generator() return self._physical_system.reset(**kwargs) diff --git a/gym_electric_motor/state_action_processors/state_noise_processor.py b/gym_electric_motor/state_action_processors/state_noise_processor.py index 545f904b..5e73d304 100644 --- a/gym_electric_motor/state_action_processors/state_noise_processor.py +++ b/gym_electric_motor/state_action_processors/state_noise_processor.py @@ -87,4 +87,4 @@ def _new_noise(self): """Samples new noise from the random distribution for the next steps.""" self._random_pointer = 0 fct = getattr(self._random_generator, self._random_dist) - self._noise = fct(size=(self._random_length, len(self._state)), **self._random_kwargs) + self._noise = fct(size=(self._random_length, len(self._state_indices)), **self._random_kwargs) diff --git a/tests/test_state_action_processors/test_dq_to_abc_action_processor.py b/tests/test_state_action_processors/test_dq_to_abc_action_processor.py new file mode 100644 index 00000000..e65573aa --- /dev/null +++ b/tests/test_state_action_processors/test_dq_to_abc_action_processor.py @@ -0,0 +1,43 @@ +import gym +import pytest +import numpy as np +import gym_electric_motor as gem + +from ..testing_utils import DummyPhysicalSystem +from .test_state_action_processor import TestStateActionProcessor + + +class TestDqToAbcActionProcessor(TestStateActionProcessor): + + @pytest.fixture + def physical_system(self): + ps = DummyPhysicalSystem(state_names=['omega', 'epsilon', 'i']) + ps.electrical_motor = gem.physical_systems.PermanentMagnetSynchronousMotor() + return ps + + @pytest.fixture + def processor(self, physical_system): + return gem.state_action_processors.DqToAbcActionProcessor(physical_system=physical_system) + + def test_action_space(self, processor, physical_system): + space = gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) + assert processor.action_space == space + + @pytest.mark.parametrize( + ['dq_action', 'state', 'abc_action'], + [ + (np.array([0.0, .0]), np.array([0.0, 0.0, 0.0]), np.array([0., 0., 0.])), + (np.array([0.0, 1.0]), np.array([12.8, 0.123, 0.0]), np.array([0.23752263, 0.72248018, -0.96000281])), + (np.array([0.0, .5]), np.array([-10.0, 0.123, 0.0]), np.array([0.49324109, -0.17566335, -0.31757774])), + ] + ) + def test_simulate(self, reset_processor, physical_system, dq_action, state, abc_action): + reset_processor._state = state + reset_processor.simulate(dq_action) + + assert all(np.isclose(reset_processor.action, abc_action)) + + + + + diff --git a/tests/test_state_action_processors/test_flux_observer.py b/tests/test_state_action_processors/test_flux_observer.py index 22581229..4ac1839e 100644 --- a/tests/test_state_action_processors/test_flux_observer.py +++ b/tests/test_state_action_processors/test_flux_observer.py @@ -41,9 +41,9 @@ def test_nominal_state(self, processor, physical_system): assert all(processor.nominal_state == np.concatenate((physical_system.nominal_state, [200., np.pi]))) def test_state_space(self, processor, physical_system): - psiabs_max = 200.0 - low = np.concatenate((physical_system.state_space.low, [-psiabs_max, -np.pi])) - high = np.concatenate((physical_system.state_space.high, [psiabs_max, np.pi])) + psi_abs_max = 200.0 + low = np.concatenate((physical_system.state_space.low, [-psi_abs_max, -np.pi])) + high = np.concatenate((physical_system.state_space.high, [psi_abs_max, np.pi])) space = gym.spaces.Box(low, high, dtype=np.float64) assert processor.state_space == space From 5dc0b65d9165de8d905a67b91b607eac1ae91c27 Mon Sep 17 00:00:00 2001 From: Arne Date: Sun, 9 Jan 2022 19:38:37 +0100 Subject: [PATCH 15/79] Added a Cookbook paragraph for the state action processors --- .../environment_features/GEM_cookbook.ipynb | 164 +++++++++--------- 1 file changed, 79 insertions(+), 85 deletions(-) diff --git a/examples/environment_features/GEM_cookbook.ipynb b/examples/environment_features/GEM_cookbook.ipynb index a5c30366..9424dd8a 100644 --- a/examples/environment_features/GEM_cookbook.ipynb +++ b/examples/environment_features/GEM_cookbook.ipynb @@ -150,17 +150,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\arnet\\anaconda3\\envs\\gem_dev\\lib\\site-packages\\gym\\logger.py:30: UserWarning: \u001b[33mWARN: Box bound precision lowered by casting to float32\u001b[0m\n", - " warnings.warn(colorize('%s: %s'%('WARN', msg % args), 'yellow'))\n" - ] - }, { "data": { "application/javascript": [ @@ -229,7 +221,9 @@ " fig.send_message('supports_binary', { value: fig.supports_binary });\n", " fig.send_message('send_image_mode', {});\n", " if (fig.ratio !== 1) {\n", - " fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", " }\n", " fig.send_message('refresh', {});\n", " };\n", @@ -608,22 +602,7 @@ "};\n", "\n", "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", - " var cursor = msg['cursor'];\n", - " switch (cursor) {\n", - " case 0:\n", - " cursor = 'pointer';\n", - " break;\n", - " case 1:\n", - " cursor = 'default';\n", - " break;\n", - " case 2:\n", - " cursor = 'crosshair';\n", - " break;\n", - " case 3:\n", - " cursor = 'move';\n", - " break;\n", - " }\n", - " fig.rubberband_canvas.style.cursor = cursor;\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", "};\n", "\n", "mpl.figure.prototype.handle_message = function (fig, msg) {\n", @@ -735,7 +714,7 @@ " };\n", "};\n", "\n", - "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", "mpl.findpos = function (e) {\n", " //this section is from http://www.quirksmode.org/js/events_properties.html\n", " var targ;\n", @@ -763,7 +742,7 @@ "/*\n", " * return a copy of an object with only non-object keys\n", " * we need this to avoid circular references\n", - " * http://stackoverflow.com/a/24161582/3208463\n", + " * https://stackoverflow.com/a/24161582/3208463\n", " */\n", "function simpleKeys(original) {\n", " return Object.keys(original).reduce(function (obj, key) {\n", @@ -852,7 +831,7 @@ "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", "// prettier-ignore\n", "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", - "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", "\n", "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", "\n", @@ -1086,11 +1065,6 @@ "};\n", "\n", "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", - " var manager = IPython.notebook.keyboard_manager;\n", - " if (!manager) {\n", - " manager = IPython.keyboard_manager;\n", - " }\n", - "\n", " // Check for shift+enter\n", " if (event.shiftKey && event.which === 13) {\n", " this.canvas_div.blur();\n", @@ -1147,7 +1121,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -1232,6 +1206,7 @@ "* tau(float)\n", "* state_filter(list(str))\n", "* callbacks(list(Callback))\n", + "* state_action_processors(iterable(StateActionProcessor))\n", "\n", "#### Environment-arg type:\n", "The `env-arg` type is a short notation for all parameters that can be passed in three different ways to override the default behavior. Exemplary, it is shown how to change the reference generator of an environment in three different ways:\n", @@ -1279,7 +1254,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -1337,7 +1312,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -1379,7 +1354,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -1401,7 +1376,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -1434,7 +1409,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -1470,7 +1445,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -1531,7 +1506,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -1560,13 +1535,13 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "from gym_electric_motor.visualization import MotorDashboard\n", "\n", - "visualization = MotorDashboard(state_plots=['i_sq', 'i_sd'], reward_plot=True) \n", + "visualization = MotorDashboard(state_plots=['i_sq', 'i_sd', 'cos(epsilon)'], reward_plot=True) \n", "# plots the states i_sd and i_sq and reward." ] }, @@ -1598,7 +1573,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -1633,7 +1608,42 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### 2.8 GEM Make Call\n", + "### 2.8 State Action Processors\n", + "State Action Processors wrap the physical system to preprocess the actions and postprocess the states of the Physical Systems.\n", + "\n", + "They are very similar to gym-wrappers, which are put around a gym-environment and process actions observations and rewards. The big difference between a gym-wrapper and a state action processor is that the state action processor is wrapped around the physical system and not around the whole environment.\n", + "\n", + "The ``CosSinProcessor`` in the following example adds the cosine and sine of the rotor angle ``epsilon`` to the state vector. These states can directly influence the calculated reward, or referenced by the reference generator and visualized by the dashboard (c.f. 2.6). \n", + "\n", + "The state action processors are passed as iterable with the argument ``state_action_processors`` as iterable (list/tuple) to the environment during the make-call (c.f. 2.9). The state action processors can also be stacked into each other. The first processor in the list is wrapped around the physical system at first and the latter ones are put around the resulting system one after the other. Therefore, outer state action processors access the processed states from the inner ones." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "from gym_electric_motor.state_action_processors import CosSinProcessor, StateNoiseProcessor\n", + "\n", + "# \n", + "state_action_processors = [\n", + " # Wrapped directly around the physical system\n", + " CosSinProcessor(angle='epsilon'),\n", + " # Wrapped around the CosSinProcessor. Therefore, the generated states (cos and sin) can be accessed.\n", + " StateNoiseProcessor(\n", + " states=['cos(epsilon)', 'sin(epsilon)'],\n", + " random_dist='normal',\n", + " random_kwargs=dict(scale=0.03)\n", + " )\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2.9 GEM Make Call\n", "\n", "The make function takes the environment-ids and several constructor arguments and returns an initialized environment. Every environment also works without further parameters with default values. These default parameters can be looked up in the [API-documentation](https://upb-lea.github.io/gym-electric-motor/index.html) of every GEM-environment.\n", "\n", @@ -1644,18 +1654,9 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\arnet\\anaconda3\\envs\\gem_dev\\lib\\site-packages\\gym\\logger.py:30: UserWarning: \u001b[33mWARN: Box bound precision lowered by casting to float32\u001b[0m\n", - " warnings.warn(colorize('%s: %s'%('WARN', msg % args), 'yellow'))\n" - ] - } - ], + "outputs": [], "source": [ "# define a PMSM with discrete action space\n", "env = gem.make( \n", @@ -1677,6 +1678,7 @@ " reference_generator=rg,\n", " ode_solver='euler',\n", " callbacks=my_callback,\n", + " state_action_processors=state_action_processors, # Pass the state action processors\n", " constraints=constraints\n", ")" ] @@ -1694,7 +1696,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -1733,9 +1735,19 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 14, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\arnet\\AppData\\Roaming\\Python\\Python39\\site-packages\\numpy\\core\\fromnumeric.py:3419: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "C:\\Users\\arnet\\AppData\\Roaming\\Python\\Python39\\site-packages\\numpy\\core\\_methods.py:188: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n" + ] + }, { "data": { "application/javascript": [ @@ -1804,7 +1816,9 @@ " fig.send_message('supports_binary', { value: fig.supports_binary });\n", " fig.send_message('send_image_mode', {});\n", " if (fig.ratio !== 1) {\n", - " fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", " }\n", " fig.send_message('refresh', {});\n", " };\n", @@ -2183,22 +2197,7 @@ "};\n", "\n", "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", - " var cursor = msg['cursor'];\n", - " switch (cursor) {\n", - " case 0:\n", - " cursor = 'pointer';\n", - " break;\n", - " case 1:\n", - " cursor = 'default';\n", - " break;\n", - " case 2:\n", - " cursor = 'crosshair';\n", - " break;\n", - " case 3:\n", - " cursor = 'move';\n", - " break;\n", - " }\n", - " fig.rubberband_canvas.style.cursor = cursor;\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", "};\n", "\n", "mpl.figure.prototype.handle_message = function (fig, msg) {\n", @@ -2310,7 +2309,7 @@ " };\n", "};\n", "\n", - "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", "mpl.findpos = function (e) {\n", " //this section is from http://www.quirksmode.org/js/events_properties.html\n", " var targ;\n", @@ -2338,7 +2337,7 @@ "/*\n", " * return a copy of an object with only non-object keys\n", " * we need this to avoid circular references\n", - " * http://stackoverflow.com/a/24161582/3208463\n", + " * https://stackoverflow.com/a/24161582/3208463\n", " */\n", "function simpleKeys(original) {\n", " return Object.keys(original).reduce(function (obj, key) {\n", @@ -2427,7 +2426,7 @@ "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", "// prettier-ignore\n", "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", - "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", "\n", "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", "\n", @@ -2661,11 +2660,6 @@ "};\n", "\n", "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", - " var manager = IPython.notebook.keyboard_manager;\n", - " if (!manager) {\n", - " manager = IPython.keyboard_manager;\n", - " }\n", - "\n", " // Check for shift+enter\n", " if (event.shiftKey && event.which === 13) {\n", " this.canvas_div.blur();\n", @@ -2722,7 +2716,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" From fbc97705e95dd325e3485895cd086bd013fa990b Mon Sep 17 00:00:00 2001 From: Arne Date: Sun, 9 Jan 2022 19:45:14 +0100 Subject: [PATCH 16/79] Changelog for state action processors --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af022571..498bcbe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] - +## Added +- State Action processors as a new feature to process actions and states directly in the environment. ## [1.0.1] - 2021-12-20 ## Added From 2574bc1bc69facf547f83c7715dfc8302a3450b4 Mon Sep 17 00:00:00 2001 From: Arne Date: Sun, 9 Jan 2022 19:51:02 +0100 Subject: [PATCH 17/79] test fix --- .../test_state_action_processors/test_state_action_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_state_action_processors/test_state_action_processor.py b/tests/test_state_action_processors/test_state_action_processor.py index 82516d92..688b4ae1 100644 --- a/tests/test_state_action_processors/test_state_action_processor.py +++ b/tests/test_state_action_processors/test_state_action_processor.py @@ -1,9 +1,9 @@ import pytest -from fontTools.ttx import process import gym_electric_motor as gem import tests.testing_utils as tu + class TestStateActionProcessor: @pytest.fixture From d8b622ed8831e54c503e917901b4ae2ef26f343f Mon Sep 17 00:00:00 2001 From: Felix Book <64723297+fbook98@users.noreply.github.com> Date: Tue, 11 Jan 2022 14:17:48 +0100 Subject: [PATCH 18/79] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 89546d28..2a6c2dfb 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setuptools.setup( name='gym_electric_motor', - version='1.0.1', + version='1.0.0', description='An OpenAI gym environment for electric motor control.', packages=setuptools.find_packages(), install_requires=requirements, From 0e1dd8a64575939b8ae7cebaa11478d86dcda812 Mon Sep 17 00:00:00 2001 From: Arne Date: Sun, 16 Jan 2022 19:33:09 +0100 Subject: [PATCH 19/79] test fixes --- .../environments/dfim/abccont_cc_dfim.rst | 2 +- .../environments/dfim/abccont_sc_dfim.rst | 2 +- .../environments/dfim/abccont_tc_dfim.rst | 2 +- .../environments/pmsm/abccont_cc_pmsm.rst | 2 +- .../environments/pmsm/abccont_sc_pmsm.rst | 2 +- .../environments/pmsm/abccont_tc_pmsm.rst | 2 +- .../environments/scim/abccont_cc_scim.rst | 2 +- .../environments/scim/abccont_sc_scim.rst | 2 +- .../environments/scim/abccont_tc_scim.rst | 2 +- .../environments/synrm/abccont_cc_synrm.rst | 2 +- .../environments/synrm/abccont_sc_synrm.rst | 2 +- .../environments/synrm/abccont_tc_synrm.rst | 2 +- gym_electric_motor/__init__.py | 108 ++------ gym_electric_motor/envs/__init__.py | 36 +-- .../cont_cc_extex_dc_env.py | 5 +- .../cont_sc_extex_dc_env.py | 5 +- .../cont_tc_extex_dc_env.py | 5 +- .../finite_cc_extex_dc_env.py | 5 +- .../finite_sc_extex_dc_env.py | 5 +- .../finite_tc_extex_dc_env.py | 5 +- .../cont_cc_permex_dc_env.py | 5 +- .../cont_sc_permex_dc_env.py | 5 +- .../cont_tc_permex_dc_env.py | 5 +- .../finite_cc_permex_dc_env.py | 5 +- .../finite_sc_permex_dc_env.py | 5 +- .../finite_tc_permex_dc_env.py | 5 +- .../cont_cc_series_dc_env.py | 5 +- .../cont_sc_series_dc_env.py | 5 +- .../cont_tc_series_dc_env.py | 5 +- .../finite_cc_series_dc_env.py | 5 +- .../finite_sc_series_dc_env.py | 5 +- .../finite_tc_series_dc_env.py | 5 +- .../cont_cc_shunt_dc_env.py | 5 +- .../cont_sc_shunt_dc_env.py | 5 +- .../cont_tc_shunt_dc_env.py | 5 +- .../finite_cc_shunt_dc_env.py | 5 +- .../finite_sc_shunt_dc_env.py | 5 +- .../finite_tc_shunt_dc_env.py | 5 +- gym_electric_motor/envs/gym_im/__init__.py | 18 +- .../__init__.py | 9 +- ...ont_cc_dfim_env.py => cont_cc_dfim_env.py} | 22 +- ...ont_sc_dfim_env.py => cont_sc_dfim_env.py} | 14 +- ...ont_tc_dfim_env.py => cont_tc_dfim_env.py} | 14 +- .../dqcont_cc_dfim_env.py | 175 ------------ .../dqcont_sc_dfim_env.py | 167 ------------ .../dqcont_tc_dfim_env.py | 171 ------------ .../finite_cc_dfim_env.py | 13 +- .../finite_sc_dfim_env.py | 5 +- .../finite_tc_dfim_env.py | 9 +- .../__init__.py | 9 +- ...ont_cc_scim_env.py => cont_cc_scim_env.py} | 12 +- ...ont_sc_scim_env.py => cont_sc_scim_env.py} | 12 +- ...ont_tc_scim_env.py => cont_tc_scim_env.py} | 12 +- .../dqcont_cc_scim_env.py | 169 ------------ .../dqcont_sc_scim_env.py | 161 ----------- .../dqcont_tc_scim_env.py | 164 ----------- .../finite_cc_scim_env.py | 9 +- .../finite_sc_scim_env.py | 5 +- .../finite_tc_scim_env.py | 5 +- gym_electric_motor/envs/gym_pmsm/__init__.py | 9 +- ...ont_cc_pmsm_env.py => cont_cc_pmsm_env.py} | 12 +- ...ont_sc_pmsm_env.py => cont_sc_pmsm_env.py} | 12 +- ...ont_tc_pmsm_env.py => cont_tc_pmsm_env.py} | 12 +- .../envs/gym_pmsm/dqcont_cc_pmsm_env.py | 164 ----------- .../envs/gym_pmsm/dqcont_sc_pmsm_env.py | 154 ----------- .../envs/gym_pmsm/dqcont_tc_pmsm_env.py | 159 ----------- .../envs/gym_pmsm/finite_cc_pmsm_env.py | 9 +- .../envs/gym_pmsm/finite_sc_pmsm_env.py | 5 +- .../envs/gym_pmsm/finite_tc_pmsm_env.py | 5 +- gym_electric_motor/envs/gym_synrm/__init__.py | 9 +- ...t_cc_synrm_env.py => cont_cc_synrm_env.py} | 16 +- ...t_sc_synrm_env.py => cont_sc_synrm_env.py} | 12 +- ...t_tc_synrm_env.py => cont_tc_synrm_env.py} | 12 +- .../envs/gym_synrm/dqcont_cc_synrm_env.py | 164 ----------- .../envs/gym_synrm/dqcont_sc_synrm_env.py | 155 ----------- .../envs/gym_synrm/dqcont_tc_synrm_env.py | 159 ----------- .../envs/gym_synrm/finite_cc_synrm_env.py | 9 +- .../envs/gym_synrm/finite_sc_synrm_env.py | 4 +- .../envs/gym_synrm/finite_tc_synrm_env.py | 5 +- .../physical_systems/__init__.py | 5 - .../physical_systems/converters.py | 51 ++-- .../physical_systems/noise_generators.py | 94 ------- .../physical_systems/physical_systems.py | 51 +--- .../state_action_processors/flux_observer.py | 1 - .../test_ac_environment_execution.py | 36 --- .../test_ac_environment_seeding.py | 123 --------- ...ution.py => test_environment_execution.py} | 4 +- ...seeding.py => test_environment_seeding.py} | 6 +- tests/test_environments/test_environments.py | 23 +- .../test_physical_systems/test_converters.py | 195 +++++-------- .../test_noise_generator.py | 258 ------------------ .../test_physical_systems.py | 6 +- tests/testing_utils.py | 19 +- 93 files changed, 286 insertions(+), 3135 deletions(-) rename gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/{abccont_cc_dfim_env.py => cont_cc_dfim_env.py} (91%) rename gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/{abccont_sc_dfim_env.py => cont_sc_dfim_env.py} (93%) rename gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/{abccont_tc_dfim_env.py => cont_tc_dfim_env.py} (93%) delete mode 100644 gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/dqcont_cc_dfim_env.py delete mode 100644 gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/dqcont_sc_dfim_env.py delete mode 100644 gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/dqcont_tc_dfim_env.py rename gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/{abccont_cc_scim_env.py => cont_cc_scim_env.py} (94%) rename gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/{abccont_sc_scim_env.py => cont_sc_scim_env.py} (94%) rename gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/{abccont_tc_scim_env.py => cont_tc_scim_env.py} (94%) delete mode 100644 gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_cc_scim_env.py delete mode 100644 gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_sc_scim_env.py delete mode 100644 gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_tc_scim_env.py rename gym_electric_motor/envs/gym_pmsm/{abccont_cc_pmsm_env.py => cont_cc_pmsm_env.py} (94%) rename gym_electric_motor/envs/gym_pmsm/{abccont_sc_pmsm_env.py => cont_sc_pmsm_env.py} (94%) rename gym_electric_motor/envs/gym_pmsm/{abccont_tc_pmsm_env.py => cont_tc_pmsm_env.py} (94%) delete mode 100644 gym_electric_motor/envs/gym_pmsm/dqcont_cc_pmsm_env.py delete mode 100644 gym_electric_motor/envs/gym_pmsm/dqcont_sc_pmsm_env.py delete mode 100644 gym_electric_motor/envs/gym_pmsm/dqcont_tc_pmsm_env.py rename gym_electric_motor/envs/gym_synrm/{abccont_cc_synrm_env.py => cont_cc_synrm_env.py} (93%) rename gym_electric_motor/envs/gym_synrm/{abccont_sc_synrm_env.py => cont_sc_synrm_env.py} (94%) rename gym_electric_motor/envs/gym_synrm/{abccont_tc_synrm_env.py => cont_tc_synrm_env.py} (94%) delete mode 100644 gym_electric_motor/envs/gym_synrm/dqcont_cc_synrm_env.py delete mode 100644 gym_electric_motor/envs/gym_synrm/dqcont_sc_synrm_env.py delete mode 100644 gym_electric_motor/envs/gym_synrm/dqcont_tc_synrm_env.py delete mode 100644 gym_electric_motor/physical_systems/noise_generators.py delete mode 100644 tests/integration_tests/test_ac_environment_execution.py delete mode 100644 tests/integration_tests/test_ac_environment_seeding.py rename tests/integration_tests/{test_dc_environment_execution.py => test_environment_execution.py} (92%) rename tests/integration_tests/{test_dc_environment_seeding.py => test_environment_seeding.py} (95%) delete mode 100644 tests/test_physical_systems/test_noise_generator.py diff --git a/docs/parts/environments/dfim/abccont_cc_dfim.rst b/docs/parts/environments/dfim/abccont_cc_dfim.rst index 74a6a0e9..54c02b70 100644 --- a/docs/parts/environments/dfim/abccont_cc_dfim.rst +++ b/docs/parts/environments/dfim/abccont_cc_dfim.rst @@ -1,4 +1,4 @@ Abc-Continuous Current Control Doubly Fed Induction Motor Environment ********************************************************************** -.. autoclass:: gym_electric_motor.envs.AbcContCurrentControlDoublyFedInductionMotorEnv +.. autoclass:: gym_electric_motor.envs.ContCurrentControlDoublyFedInductionMotorEnv :members: diff --git a/docs/parts/environments/dfim/abccont_sc_dfim.rst b/docs/parts/environments/dfim/abccont_sc_dfim.rst index 258af400..a72aef2b 100644 --- a/docs/parts/environments/dfim/abccont_sc_dfim.rst +++ b/docs/parts/environments/dfim/abccont_sc_dfim.rst @@ -1,4 +1,4 @@ Abc-Continuous Speed Control Doubly Fed Induction Motor Environment ********************************************************************** -.. autoclass:: gym_electric_motor.envs.AbcContSpeedControlDoublyFedInductionMotorEnv +.. autoclass:: gym_electric_motor.envs.ContSpeedControlDoublyFedInductionMotorEnv :members: diff --git a/docs/parts/environments/dfim/abccont_tc_dfim.rst b/docs/parts/environments/dfim/abccont_tc_dfim.rst index da02516d..66c43d6d 100644 --- a/docs/parts/environments/dfim/abccont_tc_dfim.rst +++ b/docs/parts/environments/dfim/abccont_tc_dfim.rst @@ -1,4 +1,4 @@ Abc-Continuous Torque Control Doubly Fed Induction Motor Environment ********************************************************************** -.. autoclass:: gym_electric_motor.envs.AbcContTorqueControlDoublyFedInductionMotorEnv +.. autoclass:: gym_electric_motor.envs.ContTorqueControlDoublyFedInductionMotorEnv :members: diff --git a/docs/parts/environments/pmsm/abccont_cc_pmsm.rst b/docs/parts/environments/pmsm/abccont_cc_pmsm.rst index c5eac247..bb33a15c 100644 --- a/docs/parts/environments/pmsm/abccont_cc_pmsm.rst +++ b/docs/parts/environments/pmsm/abccont_cc_pmsm.rst @@ -1,4 +1,4 @@ Abc-Continuous Current Control Permanent Magnet Synchronous Motor Environment ***************************************************************************** -.. autoclass:: gym_electric_motor.envs.AbcContCurrentControlPermanentMagnetSynchronousMotorEnv +.. autoclass:: gym_electric_motor.envs.ContCurrentControlPermanentMagnetSynchronousMotorEnv :members: diff --git a/docs/parts/environments/pmsm/abccont_sc_pmsm.rst b/docs/parts/environments/pmsm/abccont_sc_pmsm.rst index c72754e0..fc16512d 100644 --- a/docs/parts/environments/pmsm/abccont_sc_pmsm.rst +++ b/docs/parts/environments/pmsm/abccont_sc_pmsm.rst @@ -1,4 +1,4 @@ Abc-Continuous Speed Control Permanent Magnet Synchronous Motor Environment **************************************************************************** -.. autoclass:: gym_electric_motor.envs.AbcContSpeedControlPermanentMagnetSynchronousMotorEnv +.. autoclass:: gym_electric_motor.envs.ContSpeedControlPermanentMagnetSynchronousMotorEnv :members: diff --git a/docs/parts/environments/pmsm/abccont_tc_pmsm.rst b/docs/parts/environments/pmsm/abccont_tc_pmsm.rst index 687656f1..2b771494 100644 --- a/docs/parts/environments/pmsm/abccont_tc_pmsm.rst +++ b/docs/parts/environments/pmsm/abccont_tc_pmsm.rst @@ -1,4 +1,4 @@ Abc-Continuous Torque Control Permanent Magnet Synchronous Motor Environment ****************************************************************************** -.. autoclass:: gym_electric_motor.envs.AbcContTorqueControlPermanentMagnetSynchronousMotorEnv +.. autoclass:: gym_electric_motor.envs.ContTorqueControlPermanentMagnetSynchronousMotorEnv :members: diff --git a/docs/parts/environments/scim/abccont_cc_scim.rst b/docs/parts/environments/scim/abccont_cc_scim.rst index 22654ad1..7f158002 100644 --- a/docs/parts/environments/scim/abccont_cc_scim.rst +++ b/docs/parts/environments/scim/abccont_cc_scim.rst @@ -1,4 +1,4 @@ Abc-Continuous Current Control Squirrel Cage Induction Motor Environment ************************************************************************* -.. autoclass:: gym_electric_motor.envs.AbcContCurrentControlSquirrelCageInductionMotorEnv +.. autoclass:: gym_electric_motor.envs.ContCurrentControlSquirrelCageInductionMotorEnv :members: diff --git a/docs/parts/environments/scim/abccont_sc_scim.rst b/docs/parts/environments/scim/abccont_sc_scim.rst index 378d27fd..4850f947 100644 --- a/docs/parts/environments/scim/abccont_sc_scim.rst +++ b/docs/parts/environments/scim/abccont_sc_scim.rst @@ -1,4 +1,4 @@ Abc-Continuous Speed Control Squirrel Cage Induction Motor Environment ********************************************************************** -.. autoclass:: gym_electric_motor.envs.AbcContSpeedControlSquirrelCageInductionMotorEnv +.. autoclass:: gym_electric_motor.envs.ContSpeedControlSquirrelCageInductionMotorEnv :members: diff --git a/docs/parts/environments/scim/abccont_tc_scim.rst b/docs/parts/environments/scim/abccont_tc_scim.rst index a47b6982..2c51886b 100644 --- a/docs/parts/environments/scim/abccont_tc_scim.rst +++ b/docs/parts/environments/scim/abccont_tc_scim.rst @@ -1,4 +1,4 @@ Abc-Continuous Torque Control Squirrel Cage Induction Motor Environment ************************************************************************ -.. autoclass:: gym_electric_motor.envs.AbcContTorqueControlSquirrelCageInductionMotorEnv +.. autoclass:: gym_electric_motor.envs.ContTorqueControlSquirrelCageInductionMotorEnv :members: diff --git a/docs/parts/environments/synrm/abccont_cc_synrm.rst b/docs/parts/environments/synrm/abccont_cc_synrm.rst index b430f4da..6f28407a 100644 --- a/docs/parts/environments/synrm/abccont_cc_synrm.rst +++ b/docs/parts/environments/synrm/abccont_cc_synrm.rst @@ -1,4 +1,4 @@ Abc-Continuous Current Control Synchronous Reluctance Motor Environment ***************************************************************************** -.. autoclass:: gym_electric_motor.envs.AbcContCurrentControlSynchronousReluctanceMotorEnv +.. autoclass:: gym_electric_motor.envs.ContCurrentControlSynchronousReluctanceMotorEnv :members: diff --git a/docs/parts/environments/synrm/abccont_sc_synrm.rst b/docs/parts/environments/synrm/abccont_sc_synrm.rst index b7b7c2c1..32d4a1d9 100644 --- a/docs/parts/environments/synrm/abccont_sc_synrm.rst +++ b/docs/parts/environments/synrm/abccont_sc_synrm.rst @@ -1,4 +1,4 @@ Abc-Continuous Speed Control Synchronous Reluctance Motor Environment **************************************************************************** -.. autoclass:: gym_electric_motor.envs.AbcContSpeedControlSynchronousReluctanceMotorEnv +.. autoclass:: gym_electric_motor.envs.ContSpeedControlSynchronousReluctanceMotorEnv :members: diff --git a/docs/parts/environments/synrm/abccont_tc_synrm.rst b/docs/parts/environments/synrm/abccont_tc_synrm.rst index a212a376..6136d71f 100644 --- a/docs/parts/environments/synrm/abccont_tc_synrm.rst +++ b/docs/parts/environments/synrm/abccont_tc_synrm.rst @@ -1,4 +1,4 @@ Abc-Continuous Torque Control Synchronous Reluctance Motor Environment ****************************************************************************** -.. autoclass:: gym_electric_motor.envs.AbcContTorqueControlSynchronousReluctanceMotorEnv +.. autoclass:: gym_electric_motor.envs.ContTorqueControlSynchronousReluctanceMotorEnv :members: diff --git a/gym_electric_motor/__init__.py b/gym_electric_motor/__init__.py index c5f0ff44..06d55e68 100644 --- a/gym_electric_motor/__init__.py +++ b/gym_electric_motor/__init__.py @@ -174,33 +174,18 @@ **registration_kwargs ) register( - id='AbcCont-CC-PMSM-v0', - entry_point=envs_path+'AbcContCurrentControlPermanentMagnetSynchronousMotorEnv', + id='Cont-CC-PMSM-v0', + entry_point=envs_path+'ContCurrentControlPermanentMagnetSynchronousMotorEnv', **registration_kwargs ) register( - id='AbcCont-TC-PMSM-v0', - entry_point=envs_path+'AbcContTorqueControlPermanentMagnetSynchronousMotorEnv', + id='Cont-TC-PMSM-v0', + entry_point=envs_path+'ContTorqueControlPermanentMagnetSynchronousMotorEnv', **registration_kwargs ) register( - id='AbcCont-SC-PMSM-v0', - entry_point=envs_path+'AbcContSpeedControlPermanentMagnetSynchronousMotorEnv', - **registration_kwargs -) -register( - id='DqCont-CC-PMSM-v0', - entry_point=envs_path+'DqContCurrentControlPermanentMagnetSynchronousMotorEnv', - **registration_kwargs -) -register( - id='DqCont-TC-PMSM-v0', - entry_point=envs_path+'DqContTorqueControlPermanentMagnetSynchronousMotorEnv', - **registration_kwargs -) -register( - id='DqCont-SC-PMSM-v0', - entry_point=envs_path+'DqContSpeedControlPermanentMagnetSynchronousMotorEnv', + id='Cont-SC-PMSM-v0', + entry_point=envs_path+'ContSpeedControlPermanentMagnetSynchronousMotorEnv', **registration_kwargs ) @@ -221,33 +206,18 @@ **registration_kwargs ) register( - id='AbcCont-CC-SynRM-v0', - entry_point=envs_path+'AbcContCurrentControlSynchronousReluctanceMotorEnv', - **registration_kwargs -) -register( - id='AbcCont-TC-SynRM-v0', - entry_point=envs_path+'AbcContTorqueControlSynchronousReluctanceMotorEnv', - **registration_kwargs -) -register( - id='AbcCont-SC-SynRM-v0', - entry_point=envs_path+'AbcContSpeedControlSynchronousReluctanceMotorEnv', - **registration_kwargs -) -register( - id='DqCont-CC-SynRM-v0', - entry_point=envs_path+'DqContCurrentControlSynchronousReluctanceMotorEnv', + id='Cont-CC-SynRM-v0', + entry_point=envs_path+'ContCurrentControlSynchronousReluctanceMotorEnv', **registration_kwargs ) register( - id='DqCont-TC-SynRM-v0', - entry_point=envs_path+'DqContTorqueControlSynchronousReluctanceMotorEnv', + id='Cont-TC-SynRM-v0', + entry_point=envs_path+'ContTorqueControlSynchronousReluctanceMotorEnv', **registration_kwargs ) register( - id='DqCont-SC-SynRM-v0', - entry_point=envs_path+'DqContSpeedControlSynchronousReluctanceMotorEnv', + id='Cont-SC-SynRM-v0', + entry_point=envs_path+'ContSpeedControlSynchronousReluctanceMotorEnv', **registration_kwargs ) @@ -268,33 +238,18 @@ **registration_kwargs ) register( - id='AbcCont-CC-SCIM-v0', - entry_point=envs_path+'AbcContCurrentControlSquirrelCageInductionMotorEnv', + id='Cont-CC-SCIM-v0', + entry_point=envs_path+'ContCurrentControlSquirrelCageInductionMotorEnv', **registration_kwargs ) register( - id='AbcCont-TC-SCIM-v0', - entry_point=envs_path+'AbcContTorqueControlSquirrelCageInductionMotorEnv', + id='Cont-TC-SCIM-v0', + entry_point=envs_path+'ContTorqueControlSquirrelCageInductionMotorEnv', **registration_kwargs ) register( - id='AbcCont-SC-SCIM-v0', - entry_point=envs_path+'AbcContSpeedControlSquirrelCageInductionMotorEnv', - **registration_kwargs -) -register( - id='DqCont-CC-SCIM-v0', - entry_point=envs_path+'DqContCurrentControlSquirrelCageInductionMotorEnv', - **registration_kwargs -) -register( - id='DqCont-TC-SCIM-v0', - entry_point=envs_path+'DqContTorqueControlSquirrelCageInductionMotorEnv', - **registration_kwargs -) -register( - id='DqCont-SC-SCIM-v0', - entry_point=envs_path+'DqContSpeedControlSquirrelCageInductionMotorEnv', + id='Cont-SC-SCIM-v0', + entry_point=envs_path+'ContSpeedControlSquirrelCageInductionMotorEnv', **registration_kwargs ) @@ -315,32 +270,17 @@ **registration_kwargs ) register( - id='AbcCont-CC-DFIM-v0', - entry_point=envs_path+'AbcContCurrentControlDoublyFedInductionMotorEnv', - **registration_kwargs -) -register( - id='AbcCont-TC-DFIM-v0', - entry_point=envs_path+'AbcContTorqueControlDoublyFedInductionMotorEnv', - **registration_kwargs -) -register( - id='AbcCont-SC-DFIM-v0', - entry_point=envs_path+'AbcContSpeedControlDoublyFedInductionMotorEnv', - **registration_kwargs -) -register( - id='DqCont-CC-DFIM-v0', - entry_point=envs_path+'DqContCurrentControlDoublyFedInductionMotorEnv', + id='Cont-CC-DFIM-v0', + entry_point=envs_path+'ContCurrentControlDoublyFedInductionMotorEnv', **registration_kwargs ) register( - id='DqCont-TC-DFIM-v0', - entry_point=envs_path+'DqContTorqueControlDoublyFedInductionMotorEnv', + id='Cont-TC-DFIM-v0', + entry_point=envs_path+'ContTorqueControlDoublyFedInductionMotorEnv', **registration_kwargs ) register( - id='DqCont-SC-DFIM-v0', - entry_point=envs_path+'DqContSpeedControlDoublyFedInductionMotorEnv', + id='Cont-SC-DFIM-v0', + entry_point=envs_path+'ContSpeedControlDoublyFedInductionMotorEnv', **registration_kwargs ) diff --git a/gym_electric_motor/envs/__init__.py b/gym_electric_motor/envs/__init__.py index d0d566fb..f93e4556 100644 --- a/gym_electric_motor/envs/__init__.py +++ b/gym_electric_motor/envs/__init__.py @@ -30,39 +30,27 @@ from .gym_pmsm.finite_sc_pmsm_env import FiniteSpeedControlPermanentMagnetSynchronousMotorEnv from .gym_pmsm.finite_cc_pmsm_env import FiniteCurrentControlPermanentMagnetSynchronousMotorEnv from .gym_pmsm.finite_tc_pmsm_env import FiniteTorqueControlPermanentMagnetSynchronousMotorEnv -from .gym_pmsm.abccont_cc_pmsm_env import AbcContCurrentControlPermanentMagnetSynchronousMotorEnv -from .gym_pmsm.abccont_sc_pmsm_env import AbcContSpeedControlPermanentMagnetSynchronousMotorEnv -from .gym_pmsm.abccont_tc_pmsm_env import AbcContTorqueControlPermanentMagnetSynchronousMotorEnv -from .gym_pmsm.dqcont_sc_pmsm_env import DqContSpeedControlPermanentMagnetSynchronousMotorEnv -from .gym_pmsm.dqcont_tc_pmsm_env import DqContTorqueControlPermanentMagnetSynchronousMotorEnv -from .gym_pmsm.dqcont_cc_pmsm_env import DqContCurrentControlPermanentMagnetSynchronousMotorEnv +from .gym_pmsm.cont_cc_pmsm_env import ContCurrentControlPermanentMagnetSynchronousMotorEnv +from .gym_pmsm.cont_sc_pmsm_env import ContSpeedControlPermanentMagnetSynchronousMotorEnv +from .gym_pmsm.cont_tc_pmsm_env import ContTorqueControlPermanentMagnetSynchronousMotorEnv from .gym_synrm.finite_sc_synrm_env import FiniteSpeedControlSynchronousReluctanceMotorEnv from .gym_synrm.finite_cc_synrm_env import FiniteCurrentControlSynchronousReluctanceMotorEnv from .gym_synrm.finite_tc_synrm_env import FiniteTorqueControlSynchronousReluctanceMotorEnv -from .gym_synrm.abccont_tc_synrm_env import AbcContTorqueControlSynchronousReluctanceMotorEnv -from .gym_synrm.abccont_cc_synrm_env import AbcContCurrentControlSynchronousReluctanceMotorEnv -from .gym_synrm.abccont_sc_synrm_env import AbcContSpeedControlSynchronousReluctanceMotorEnv -from .gym_synrm.dqcont_cc_synrm_env import DqContCurrentControlSynchronousReluctanceMotorEnv -from .gym_synrm.dqcont_sc_synrm_env import DqContSpeedControlSynchronousReluctanceMotorEnv -from .gym_synrm.dqcont_tc_synrm_env import DqContTorqueControlSynchronousReluctanceMotorEnv +from .gym_synrm.cont_tc_synrm_env import ContTorqueControlSynchronousReluctanceMotorEnv +from .gym_synrm.cont_cc_synrm_env import ContCurrentControlSynchronousReluctanceMotorEnv +from .gym_synrm.cont_sc_synrm_env import ContSpeedControlSynchronousReluctanceMotorEnv -from .gym_im import DqContSpeedControlSquirrelCageInductionMotorEnv -from .gym_im import DqContCurrentControlSquirrelCageInductionMotorEnv -from .gym_im import DqContTorqueControlSquirrelCageInductionMotorEnv -from .gym_im import AbcContSpeedControlSquirrelCageInductionMotorEnv -from .gym_im import AbcContCurrentControlSquirrelCageInductionMotorEnv -from .gym_im import AbcContTorqueControlSquirrelCageInductionMotorEnv +from .gym_im import ContSpeedControlSquirrelCageInductionMotorEnv +from .gym_im import ContCurrentControlSquirrelCageInductionMotorEnv +from .gym_im import ContTorqueControlSquirrelCageInductionMotorEnv from .gym_im import FiniteSpeedControlSquirrelCageInductionMotorEnv from .gym_im import FiniteCurrentControlSquirrelCageInductionMotorEnv from .gym_im import FiniteTorqueControlSquirrelCageInductionMotorEnv -from .gym_im import DqContSpeedControlDoublyFedInductionMotorEnv -from .gym_im import DqContCurrentControlDoublyFedInductionMotorEnv -from .gym_im import DqContTorqueControlDoublyFedInductionMotorEnv -from .gym_im import AbcContSpeedControlDoublyFedInductionMotorEnv -from .gym_im import AbcContCurrentControlDoublyFedInductionMotorEnv -from .gym_im import AbcContTorqueControlDoublyFedInductionMotorEnv +from .gym_im import ContSpeedControlDoublyFedInductionMotorEnv +from .gym_im import ContCurrentControlDoublyFedInductionMotorEnv +from .gym_im import ContTorqueControlDoublyFedInductionMotorEnv from .gym_im import FiniteSpeedControlDoublyFedInductionMotorEnv from .gym_im import FiniteCurrentControlDoublyFedInductionMotorEnv from .gym_im import FiniteTorqueControlDoublyFedInductionMotorEnv diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_cc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_cc_extex_dc_env.py index 577f9572..9340a0cc 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_cc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_cc_extex_dc_env.py @@ -22,7 +22,6 @@ class ContCurrentControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcExternallyExcitedMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_a', 'i_e'`` @@ -84,7 +83,7 @@ class ContCurrentControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ @@ -94,7 +93,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -139,7 +137,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.DcExternallyExcitedMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_sc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_sc_extex_dc_env.py index 4429ca21..c154385f 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_sc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_sc_extex_dc_env.py @@ -22,7 +22,6 @@ class ContSpeedControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcExternallyExcitedMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -84,7 +83,7 @@ class ContSpeedControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ @@ -94,7 +93,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -137,7 +135,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.0, b=0.0, c=0.0, j_load=1e-4) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_tc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_tc_extex_dc_env.py index 32ee4435..0c072893 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_tc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_tc_extex_dc_env.py @@ -22,7 +22,6 @@ class ContTorqueControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcExternallyExcitedMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -84,7 +83,7 @@ class ContTorqueControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i_a', 'i_e',), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ @@ -94,7 +93,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -136,7 +134,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.DcExternallyExcitedMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_cc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_cc_extex_dc_env.py index af5d5ee6..76905787 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_cc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_cc_extex_dc_env.py @@ -22,7 +22,6 @@ class FiniteCurrentControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcExternallyExcitedMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_a', 'i_e'`` @@ -84,7 +83,7 @@ class FiniteCurrentControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ @@ -94,7 +93,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -136,7 +134,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.DcExternallyExcitedMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_sc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_sc_extex_dc_env.py index 1d5c6ba2..52f88080 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_sc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_sc_extex_dc_env.py @@ -22,7 +22,6 @@ class FiniteSpeedControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcExternallyExcitedMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -84,7 +83,7 @@ class FiniteSpeedControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ @@ -94,7 +93,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -138,7 +136,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.0, b=0.0, c=0.0, j_load=1e-4) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_tc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_tc_extex_dc_env.py index c31ded16..1c078598 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_tc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_tc_extex_dc_env.py @@ -22,7 +22,6 @@ class FiniteTorqueControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcExternallyExcitedMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -84,7 +83,7 @@ class FiniteTorqueControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5,state_action_processors=()): """ @@ -94,7 +93,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -136,7 +134,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.DcExternallyExcitedMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_cc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_cc_permex_dc_env.py index 39705204..fe52b54c 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_cc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_cc_permex_dc_env.py @@ -22,7 +22,6 @@ class ContCurrentControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcPermanentlyExcitedMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i'`` @@ -85,7 +84,7 @@ class ContCurrentControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ def __init__( - self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=() ): @@ -96,7 +95,6 @@ def __init__( motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -132,7 +130,6 @@ def __init__( motor=initialize(ps.ElectricMotor, motor, ps.DcPermanentlyExcitedMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_sc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_sc_permex_dc_env.py index 1e4af44b..00901f31 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_sc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_sc_permex_dc_env.py @@ -22,7 +22,6 @@ class ContSpeedControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcPermanentlyExcitedMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -84,7 +83,7 @@ class ContSpeedControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ @@ -94,7 +93,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` dor the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -132,7 +130,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.0, b=0.0, c=0.0, j_load=1e-4) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_tc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_tc_permex_dc_env.py index c4e4c8f3..6d539df0 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_tc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_tc_permex_dc_env.py @@ -22,7 +22,6 @@ class ContTorqueControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcPermanentlyExcitedMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -85,7 +84,7 @@ class ContTorqueControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ @@ -95,7 +94,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` dor the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -131,7 +129,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.DcPermanentlyExcitedMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_cc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_cc_permex_dc_env.py index 447dce5e..8cc53c68 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_cc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_cc_permex_dc_env.py @@ -22,7 +22,6 @@ class FiniteCurrentControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment) - Motor: :py:class:`.DcPermanentlyExcitedMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i'`` @@ -85,7 +84,7 @@ class FiniteCurrentControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment) >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ def __init__( - self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=() ): @@ -96,7 +95,6 @@ def __init__( motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` dor the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -132,7 +130,6 @@ def __init__( motor=initialize(ps.ElectricMotor, motor, ps.DcPermanentlyExcitedMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_sc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_sc_permex_dc_env.py index d7a963ce..c40bee14 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_sc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_sc_permex_dc_env.py @@ -22,7 +22,6 @@ class FiniteSpeedControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcPermanentlyExcitedMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -85,7 +84,7 @@ class FiniteSpeedControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ def __init__( - self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=() ): @@ -96,7 +95,6 @@ def __init__( motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` dor the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -135,7 +133,6 @@ def __init__( load_parameter=dict(a=0.0, b=0.0, c=0.0, j_load=1e-3) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_tc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_tc_permex_dc_env.py index 2cf523bf..0a543794 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_tc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_tc_permex_dc_env.py @@ -22,7 +22,6 @@ class FiniteTorqueControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcPermanentlyExcitedMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -85,7 +84,7 @@ class FiniteTorqueControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ def __init__( - self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=() ): @@ -96,7 +95,6 @@ def __init__( motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -132,7 +130,6 @@ def __init__( motor=initialize(ps.ElectricMotor, motor, ps.DcPermanentlyExcitedMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_cc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_cc_series_dc_env.py index ded30773..9f038f7d 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_cc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_cc_series_dc_env.py @@ -22,7 +22,6 @@ class ContCurrentControlDcSeriesMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcSeriesMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i'`` @@ -84,7 +83,7 @@ class ContCurrentControlDcSeriesMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ @@ -94,7 +93,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -131,7 +129,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.DcSeriesMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_sc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_sc_series_dc_env.py index d7cd6358..25717082 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_sc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_sc_series_dc_env.py @@ -22,7 +22,6 @@ class ContSpeedControlDcSeriesMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcSeriesMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -85,7 +84,7 @@ class ContSpeedControlDcSeriesMotorEnv(ElectricMotorEnvironment): >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ @@ -95,7 +94,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -133,7 +131,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.01, b=0.05, c=0.0, j_load=1e-4) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_tc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_tc_series_dc_env.py index dc93d994..16789c50 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_tc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_tc_series_dc_env.py @@ -22,7 +22,6 @@ class ContTorqueControlDcSeriesMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcSeriesMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -85,7 +84,7 @@ class ContTorqueControlDcSeriesMotorEnv(ElectricMotorEnvironment): >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ @@ -95,7 +94,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -131,7 +129,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.DcSeriesMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_cc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_cc_series_dc_env.py index a53d459b..8deb9dfe 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_cc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_cc_series_dc_env.py @@ -22,7 +22,6 @@ class FiniteCurrentControlDcSeriesMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcSeriesMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i'`` @@ -84,7 +83,7 @@ class FiniteCurrentControlDcSeriesMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ @@ -94,7 +93,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -130,7 +128,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.DcSeriesMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_sc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_sc_series_dc_env.py index 68619bc3..0928d104 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_sc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_sc_series_dc_env.py @@ -22,7 +22,6 @@ class FiniteSpeedControlDcSeriesMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcSeriesMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -84,7 +83,7 @@ class FiniteSpeedControlDcSeriesMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ @@ -94,7 +93,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -132,7 +130,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.15, b=0.05, c=0.0, j_load=1e-4) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_tc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_tc_series_dc_env.py index dfe17b7d..43c4b3d2 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_tc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_tc_series_dc_env.py @@ -22,7 +22,6 @@ class FiniteTorqueControlDcSeriesMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcSeriesMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -84,7 +83,7 @@ class FiniteTorqueControlDcSeriesMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ @@ -94,7 +93,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -131,7 +129,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.DcSeriesMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py index a8beb128..8a25a1c9 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py @@ -23,7 +23,6 @@ class ContCurrentControlDcShuntMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcShuntMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_a'`` @@ -86,7 +85,7 @@ class ContCurrentControlDcShuntMotorEnv(ElectricMotorEnvironment): >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -133,7 +131,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.DcShuntMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_sc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_sc_shunt_dc_env.py index f9ea6ec3..171efe82 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_sc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_sc_shunt_dc_env.py @@ -23,7 +23,6 @@ class ContSpeedControlDcShuntMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcShuntMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -86,7 +85,7 @@ class ContSpeedControlDcShuntMotorEnv(ElectricMotorEnvironment): >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -135,7 +133,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.05, b=0.01, c=0.0, j_load=1e-4) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_tc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_tc_shunt_dc_env.py index 10be31c6..1166cfd5 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_tc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_tc_shunt_dc_env.py @@ -23,7 +23,6 @@ class ContTorqueControlDcShuntMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcShuntMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -86,7 +85,7 @@ class ContTorqueControlDcShuntMotorEnv(ElectricMotorEnvironment): >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): """ @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -133,7 +131,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.DcShuntMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=230.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_cc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_cc_shunt_dc_env.py index 93708016..60f97003 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_cc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_cc_shunt_dc_env.py @@ -23,7 +23,6 @@ class FiniteCurrentControlDcShuntMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcShuntMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_a'`` @@ -86,7 +85,7 @@ class FiniteCurrentControlDcShuntMotorEnv(ElectricMotorEnvironment): >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -133,7 +131,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.DcShuntMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_sc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_sc_shunt_dc_env.py index 9b0dc60b..27bb3c1e 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_sc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_sc_shunt_dc_env.py @@ -23,7 +23,6 @@ class FiniteSpeedControlDcShuntMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcShuntMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -86,7 +85,7 @@ class FiniteSpeedControlDcShuntMotorEnv(ElectricMotorEnvironment): >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -135,7 +133,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.05, b=0.01, c=0.0, j_load=1e-4) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_tc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_tc_shunt_dc_env.py index bb52fb41..4038c3cb 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_tc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_tc_shunt_dc_env.py @@ -23,7 +23,6 @@ class FiniteTorqueControlDcShuntMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DcShuntMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -86,7 +85,7 @@ class FiniteTorqueControlDcShuntMotorEnv(ElectricMotorEnvironment): >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): """ @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -132,7 +130,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.DcShuntMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_im/__init__.py b/gym_electric_motor/envs/gym_im/__init__.py index 37bcfc6a..7a3b29ba 100644 --- a/gym_electric_motor/envs/gym_im/__init__.py +++ b/gym_electric_motor/envs/gym_im/__init__.py @@ -1,19 +1,13 @@ -from .squirrel_cage_induction_motor_envs import DqContCurrentControlSquirrelCageInductionMotorEnv -from .squirrel_cage_induction_motor_envs import DqContSpeedControlSquirrelCageInductionMotorEnv -from .squirrel_cage_induction_motor_envs import DqContTorqueControlSquirrelCageInductionMotorEnv -from .squirrel_cage_induction_motor_envs import AbcContCurrentControlSquirrelCageInductionMotorEnv -from .squirrel_cage_induction_motor_envs import AbcContSpeedControlSquirrelCageInductionMotorEnv -from .squirrel_cage_induction_motor_envs import AbcContTorqueControlSquirrelCageInductionMotorEnv +from .squirrel_cage_induction_motor_envs import ContCurrentControlSquirrelCageInductionMotorEnv +from .squirrel_cage_induction_motor_envs import ContSpeedControlSquirrelCageInductionMotorEnv +from .squirrel_cage_induction_motor_envs import ContTorqueControlSquirrelCageInductionMotorEnv from .squirrel_cage_induction_motor_envs import FiniteCurrentControlSquirrelCageInductionMotorEnv from .squirrel_cage_induction_motor_envs import FiniteSpeedControlSquirrelCageInductionMotorEnv from .squirrel_cage_induction_motor_envs import FiniteTorqueControlSquirrelCageInductionMotorEnv -from .doubly_fed_induction_motor_envs import DqContCurrentControlDoublyFedInductionMotorEnv -from .doubly_fed_induction_motor_envs import DqContSpeedControlDoublyFedInductionMotorEnv -from .doubly_fed_induction_motor_envs import DqContTorqueControlDoublyFedInductionMotorEnv -from .doubly_fed_induction_motor_envs import AbcContCurrentControlDoublyFedInductionMotorEnv -from .doubly_fed_induction_motor_envs import AbcContSpeedControlDoublyFedInductionMotorEnv -from .doubly_fed_induction_motor_envs import AbcContTorqueControlDoublyFedInductionMotorEnv +from .doubly_fed_induction_motor_envs import ContCurrentControlDoublyFedInductionMotorEnv +from .doubly_fed_induction_motor_envs import ContSpeedControlDoublyFedInductionMotorEnv +from .doubly_fed_induction_motor_envs import ContTorqueControlDoublyFedInductionMotorEnv from .doubly_fed_induction_motor_envs import FiniteCurrentControlDoublyFedInductionMotorEnv from .doubly_fed_induction_motor_envs import FiniteSpeedControlDoublyFedInductionMotorEnv from .doubly_fed_induction_motor_envs import FiniteTorqueControlDoublyFedInductionMotorEnv diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/__init__.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/__init__.py index 45b91854..d7ce4715 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/__init__.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/__init__.py @@ -1,9 +1,6 @@ -from .dqcont_cc_dfim_env import DqContCurrentControlDoublyFedInductionMotorEnv -from .dqcont_tc_dfim_env import DqContTorqueControlDoublyFedInductionMotorEnv -from .dqcont_sc_dfim_env import DqContSpeedControlDoublyFedInductionMotorEnv -from .abccont_sc_dfim_env import AbcContSpeedControlDoublyFedInductionMotorEnv -from .abccont_tc_dfim_env import AbcContTorqueControlDoublyFedInductionMotorEnv -from .abccont_cc_dfim_env import AbcContCurrentControlDoublyFedInductionMotorEnv +from .cont_sc_dfim_env import ContSpeedControlDoublyFedInductionMotorEnv +from .cont_tc_dfim_env import ContTorqueControlDoublyFedInductionMotorEnv +from .cont_cc_dfim_env import ContCurrentControlDoublyFedInductionMotorEnv from .finite_tc_dfim_env import FiniteTorqueControlDoublyFedInductionMotorEnv from .finite_sc_dfim_env import FiniteSpeedControlDoublyFedInductionMotorEnv from .finite_cc_dfim_env import FiniteCurrentControlDoublyFedInductionMotorEnv diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/abccont_cc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_cc_dfim_env.py similarity index 91% rename from gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/abccont_cc_dfim_env.py rename to gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_cc_dfim_env.py index bcbba7d1..d17681c0 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/abccont_cc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_cc_dfim_env.py @@ -9,13 +9,13 @@ from gym_electric_motor.constraints import SquaredConstraint -class AbcContCurrentControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): +class ContCurrentControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): """ Description: Environment to simulate a abc-domain continuous control set current controlled doubly fed induction motor. Key: - ``'AbcCont-CC-DFIM-v0'`` + ``'Cont-CC-DFIM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` @@ -23,7 +23,6 @@ class AbcContCurrentControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DoublyFedInductionMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` @@ -59,7 +58,7 @@ class AbcContCurrentControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): Box(low=[-1, -1], high=[1, 1]) Action Space: - Box(low=[-1, -1, -1], high=[1, 1, 1]) + Box(low=[-1] * 6, high=[1] * 6) Initial State: Zeros on all state variables. @@ -80,7 +79,7 @@ class AbcContCurrentControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-CC-DFIM-v0', + ... 'Cont-CC-DFIM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -93,7 +92,7 @@ class AbcContCurrentControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ def __init__( - self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4 ): @@ -104,7 +103,6 @@ def __init__( motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -132,11 +130,11 @@ def __init__( This class is then initialized with its default parameters. The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ - default_subgenerators = ( + default_sub_generators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), WienerProcessReferenceGenerator(reference_state='i_sq') ) - default_subconverters = ( + default_sub_converters = ( ps.ContB6BridgeConverter(), ps.ContB6BridgeConverter() ) @@ -147,21 +145,19 @@ def __init__( ps.PowerElectronicConverter, converter, ps.ContMultiConverter, - dict(subconverters=default_subconverters) + dict(subconverters=default_sub_converters) ), motor=initialize(ps.ElectricMotor, motor, ps.DoublyFedInductionMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='abc' ) reference_generator = initialize( ReferenceGenerator, reference_generator, MultipleReferenceGenerator, - dict(sub_generators=default_subgenerators) + dict(sub_generators=default_sub_generators) ) reward_function = initialize( RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/abccont_sc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_sc_dfim_env.py similarity index 93% rename from gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/abccont_sc_dfim_env.py rename to gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_sc_dfim_env.py index 234908dc..db93d10b 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/abccont_sc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_sc_dfim_env.py @@ -9,13 +9,13 @@ from gym_electric_motor.constraints import SquaredConstraint -class AbcContSpeedControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): +class ContSpeedControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): """ Description: Environment to simulate a abc-domain continuous control set current controlled doubly fed induction motor. Key: - ``'AbcCont-SC-DFIM-v0'`` + ``'Cont-SC-DFIM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` @@ -23,7 +23,6 @@ class AbcContSpeedControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DoublyFedInductionMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -80,7 +79,7 @@ class AbcContSpeedControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-SC-DFIM-v0', + ... 'Cont-SC-DFIM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -102,7 +101,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -130,7 +128,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve This class is then initialized with its default parameters. The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ - default_subconverters = ( + default_sub_converters = ( ps.ContB6BridgeConverter(), ps.ContB6BridgeConverter() ) @@ -140,17 +138,15 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ps.PowerElectronicConverter, converter, ps.ContMultiConverter, - dict(subconverters=default_subconverters) + dict(subconverters=default_sub_converters) ), motor=initialize(ps.ElectricMotor, motor, ps.DoublyFedInductionMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict( load_parameter=dict(a=0.01, b=0.01, c=0.0) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='abc', ) reference_generator = initialize( ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/abccont_tc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_tc_dfim_env.py similarity index 93% rename from gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/abccont_tc_dfim_env.py rename to gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_tc_dfim_env.py index 71feede4..2e29b144 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/abccont_tc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_tc_dfim_env.py @@ -9,13 +9,13 @@ from gym_electric_motor.constraints import SquaredConstraint -class AbcContTorqueControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): +class ContTorqueControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): """ Description: Environment to simulate a abc-domain continuous control set torque controlled doubly fed induction motor. Key: - ``'AbcCont-TC-DFIM-v0'`` + ``'Cont-TC-DFIM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` @@ -23,7 +23,6 @@ class AbcContTorqueControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DoublyFedInductionMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -80,7 +79,7 @@ class AbcContTorqueControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-TC-DFIM-v0', + ... 'Cont-TC-DFIM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -102,7 +101,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -130,7 +128,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve This class is then initialized with its default parameters. The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ - default_subconverters = ( + default_sub_converters = ( ps.ContB6BridgeConverter(), ps.ContB6BridgeConverter() ) @@ -140,15 +138,13 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ps.PowerElectronicConverter, converter, ps.ContMultiConverter, - dict(subconverters=default_subconverters) + dict(subconverters=default_sub_converters) ), motor=initialize(ps.ElectricMotor, motor, ps.DoublyFedInductionMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='abc' ) reference_generator = initialize( ReferenceGenerator, diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/dqcont_cc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/dqcont_cc_dfim_env.py deleted file mode 100644 index e745f7b4..00000000 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/dqcont_cc_dfim_env.py +++ /dev/null @@ -1,175 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import DoublyFedInductionMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContCurrentControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain continuous control set current controlled doubly fed induction motor. - - Key: - ``'DqCont-CC-DFIM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.DoublyFedInductionMotor` - - Load: :py:class:`.ConstantSpeedLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'i_sd' = 0.5, 'i_sq' = 0.5`` - - - Visualization: :py:class:`.MotorDashboard` current and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``[ - 'omega' , 'torque', - 'i_sa', 'i_sb', 'i_sc', 'i_sd', 'i_sq', - 'u_sa', 'u_sb', 'u_sc', 'u_sd', 'u_sq', - 'i_ra', 'i_rb', 'i_rc', 'i_rd', 'i_rq', - 'u_ra', 'u_rb', 'u_rc', 'u_rd', 'u_rq', - 'epsilon', 'u_sup' - ]`` - - Reference Variables: - ``['i_sd', 'i_sq']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=24 * [-1], high=24 * [1]) - - Reference Space: - Box(low=[-1, -1], high=[1, 1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='i_sq', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'DqCont-CC-DFIM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - default_subgenerators = ( - WienerProcessReferenceGenerator(reference_state='i_sd'), - WienerProcessReferenceGenerator(reference_state='i_sq') - ) - default_subconverters = ( - ps.ContB6BridgeConverter(), - ps.ContB6BridgeConverter() - ) - physical_system = DoublyFedInductionMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize( - ps.PowerElectronicConverter, - converter, - ps.ContMultiConverter, - dict(subconverters=default_subconverters) - ), - motor=initialize(ps.ElectricMotor, motor, ps.DoublyFedInductionMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq' - ) - reference_generator = initialize( - ReferenceGenerator, - reference_generator, - MultipleReferenceGenerator, - dict(sub_generators=default_subgenerators) - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) - ) - visualization = initialize( - ElectricMotorVisualization, - visualization, - MotorDashboard, - dict(state_plots=('i_sd', 'i_sq'), action_plots='all') - ) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks - ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/dqcont_sc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/dqcont_sc_dfim_env.py deleted file mode 100644 index a2dddc66..00000000 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/dqcont_sc_dfim_env.py +++ /dev/null @@ -1,167 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import DoublyFedInductionMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContSpeedControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain continuous control set current controlled doubly fed induction motor. - - Key: - ``'DqCont-SC-DFIM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.DoublyFedInductionMotor` - - Load: :py:class:`.PolynomialStaticLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'omega' = 1.0`` - - - Visualization: :py:class:`.MotorDashboard` speed and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``[ - 'omega' , 'torque', - 'i_sa', 'i_sb', 'i_sc', 'i_sd', 'i_sq', - 'u_sa', 'u_sb', 'u_sc', 'u_sd', 'u_sq', - 'i_ra', 'i_rb', 'i_rc', 'i_rd', 'i_rq', - 'u_ra', 'u_rb', 'u_rc', 'u_rd', 'u_rq', - 'epsilon', 'u_sup' - ]`` - - Reference Variables: - ``['omega']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=24 * [-1], high=24 * [1]) - - Reference Space: - Box(low=[-1], high=[1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='omega', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'DqCont-SC-DFIM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - default_subconverters = ( - ps.ContB6BridgeConverter(), - ps.ContB6BridgeConverter() - ) - physical_system = DoublyFedInductionMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize( - ps.PowerElectronicConverter, - converter, - ps.ContMultiConverter, - dict(subconverters=default_subconverters) - ), - motor=initialize(ps.ElectricMotor, motor, ps.DoublyFedInductionMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict( - load_parameter=dict(a=0.01, b=0.01, c=0.0) - )), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq', - ) - reference_generator = initialize( - ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, - dict(reference_state='omega', sigma_range=(1e-3, 1e-2)), - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(omega=1.0)) - ) - visualization = initialize( - ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks - ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/dqcont_tc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/dqcont_tc_dfim_env.py deleted file mode 100644 index 4aa50095..00000000 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/dqcont_tc_dfim_env.py +++ /dev/null @@ -1,171 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import DoublyFedInductionMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContTorqueControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain continuous control set torque controlled doubly fed induction motor. - - Key: - ``'DqCont-TC-DFIM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.DoublyFedInductionMotor` - - Load: :py:class:`.ConstantSpeedLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'torque' = 1.0`` - - - Visualization: :py:class:`.MotorDashboard` torque and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``[ - 'omega' , 'torque', - 'i_sa', 'i_sb', 'i_sc', 'i_sd', 'i_sq', - 'u_sa', 'u_sb', 'u_sc', 'u_sd', 'u_sq', - 'i_ra', 'i_rb', 'i_rc', 'i_rd', 'i_rq', - 'u_ra', 'u_rb', 'u_rc', 'u_rd', 'u_rq', - 'epsilon', 'u_sup' - ]`` - - Reference Variables: - ``['torque']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=24 * [-1], high=24 * [1]) - - Reference Space: - Box(low=[-1], high=[1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='torque', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'AbcCont-TC-DFIM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - default_subconverters = ( - ps.ContB6BridgeConverter(), - ps.ContB6BridgeConverter() - ) - physical_system = DoublyFedInductionMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize( - ps.PowerElectronicConverter, - converter, - ps.ContMultiConverter, - dict(subconverters=default_subconverters) - ), - motor=initialize(ps.ElectricMotor, motor, ps.DoublyFedInductionMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq' - ) - reference_generator = initialize( - ReferenceGenerator, - reference_generator, - WienerProcessReferenceGenerator, - dict(reference_state='torque') - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(torque=1.0)) - ) - visualization = initialize( - ElectricMotorVisualization, - visualization, - MotorDashboard, - dict(state_plots=('torque',), action_plots='all') - ) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks - ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_cc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_cc_dfim_env.py index 4170d691..1eaeb61f 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_cc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_cc_dfim_env.py @@ -23,7 +23,6 @@ class FiniteCurrentControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DoublyFedInductionMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` @@ -92,7 +91,7 @@ class FiniteCurrentControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): """ @@ -102,7 +101,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -130,11 +128,11 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve This class is then initialized with its default parameters. The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ - default_subgenerators = ( + default_sub_generators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), WienerProcessReferenceGenerator(reference_state='i_sq') ) - default_subconverters = ( + default_sub_converters = ( ps.FiniteB6BridgeConverter(), ps.FiniteB6BridgeConverter() ) @@ -145,12 +143,11 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ps.PowerElectronicConverter, converter, ps.FiniteMultiConverter, - dict(subconverters=default_subconverters) + dict(subconverters=default_sub_converters) ), motor=initialize(ps.ElectricMotor, motor, ps.DoublyFedInductionMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) @@ -158,7 +155,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ReferenceGenerator, reference_generator, MultipleReferenceGenerator, - dict(sub_generators=default_subgenerators) + dict(sub_generators=default_sub_generators) ) reward_function = initialize( RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_sc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_sc_dfim_env.py index f66bd15f..a32c7aa4 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_sc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_sc_dfim_env.py @@ -23,7 +23,6 @@ class FiniteSpeedControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DoublyFedInductionMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -92,7 +91,7 @@ class FiniteSpeedControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): """ @@ -102,7 +101,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -147,7 +145,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.01, b=0.01, c=0.0) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_tc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_tc_dfim_env.py index b7d66417..63672139 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_tc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_tc_dfim_env.py @@ -23,7 +23,6 @@ class FiniteTorqueControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.DoublyFedInductionMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -92,7 +91,7 @@ class FiniteTorqueControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): """ @@ -102,7 +101,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -130,7 +128,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve This class is then initialized with its default parameters. The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ - default_subconverters = ( + default_sub_converters = ( ps.FiniteB6BridgeConverter(), ps.FiniteB6BridgeConverter() ) @@ -140,12 +138,11 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ps.PowerElectronicConverter, converter, ps.FiniteMultiConverter, - dict(subconverters=default_subconverters) + dict(subconverters=default_sub_converters) ), motor=initialize(ps.ElectricMotor, motor, ps.DoublyFedInductionMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/__init__.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/__init__.py index b88857d2..f48e17bb 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/__init__.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/__init__.py @@ -1,9 +1,6 @@ -from .dqcont_cc_scim_env import DqContCurrentControlSquirrelCageInductionMotorEnv -from .dqcont_tc_scim_env import DqContTorqueControlSquirrelCageInductionMotorEnv -from .dqcont_sc_scim_env import DqContSpeedControlSquirrelCageInductionMotorEnv -from .abccont_sc_scim_env import AbcContSpeedControlSquirrelCageInductionMotorEnv -from .abccont_tc_scim_env import AbcContTorqueControlSquirrelCageInductionMotorEnv -from .abccont_cc_scim_env import AbcContCurrentControlSquirrelCageInductionMotorEnv +from .cont_sc_scim_env import ContSpeedControlSquirrelCageInductionMotorEnv +from .cont_tc_scim_env import ContTorqueControlSquirrelCageInductionMotorEnv +from .cont_cc_scim_env import ContCurrentControlSquirrelCageInductionMotorEnv from .finite_tc_scim_env import FiniteTorqueControlSquirrelCageInductionMotorEnv from .finite_sc_scim_env import FiniteSpeedControlSquirrelCageInductionMotorEnv from .finite_cc_scim_env import FiniteCurrentControlSquirrelCageInductionMotorEnv diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_cc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_cc_scim_env.py similarity index 94% rename from gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_cc_scim_env.py rename to gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_cc_scim_env.py index 871fca01..86a29d31 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_cc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_cc_scim_env.py @@ -9,13 +9,13 @@ from gym_electric_motor.constraints import SquaredConstraint -class AbcContCurrentControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): +class ContCurrentControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): """ Description: Environment to simulate an abc-domain cont. control set current controlled squirrel cage induction motor. Key: - ``'AbcCont-CC-SCIM-v0'`` + ``'Cont-CC-SCIM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` @@ -23,7 +23,6 @@ class AbcContCurrentControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironmen - Motor: :py:class:`.SquirrelCageInductionMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` @@ -78,7 +77,7 @@ class AbcContCurrentControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironmen ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-CC-SCIM-v0', + ... 'Cont-CC-SCIM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -90,7 +89,7 @@ class AbcContCurrentControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironmen >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, state_action_processors=()): @@ -101,7 +100,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -142,10 +140,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.SquirrelCageInductionMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='abc' ) reference_generator = initialize( ReferenceGenerator, diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_sc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_sc_scim_env.py similarity index 94% rename from gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_sc_scim_env.py rename to gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_sc_scim_env.py index 83bd0af1..14d3948f 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_sc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_sc_scim_env.py @@ -9,13 +9,13 @@ from gym_electric_motor.constraints import SquaredConstraint -class AbcContSpeedControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): +class ContSpeedControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): """ Description: Environment to simulate an abc-domain cont. control set speed controlled squirrel cage induction motor. Key: - ``'AbcCont-SC-SCIM-v0'`` + ``'Cont-SC-SCIM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` @@ -23,7 +23,6 @@ class AbcContSpeedControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment) - Motor: :py:class:`.SquirrelCageInductionMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -78,7 +77,7 @@ class AbcContSpeedControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment) ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-SC-SCIM-v0', + ... 'Cont-SC-SCIM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -90,7 +89,7 @@ class AbcContSpeedControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment) >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), state_action_processors=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): @@ -101,7 +100,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -140,10 +138,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.01, b=0.01, c=0.0) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='abc', ) reference_generator = initialize( ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_tc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_tc_scim_env.py similarity index 94% rename from gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_tc_scim_env.py rename to gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_tc_scim_env.py index 0de74cf8..c75feafe 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/abccont_tc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_tc_scim_env.py @@ -9,13 +9,13 @@ from gym_electric_motor.constraints import SquaredConstraint -class AbcContTorqueControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): +class ContTorqueControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): """ Description: Environment to simulate an abc-domain cont. control set torque controlled squirrel cage induction motor. Key: - ``'AbcCont-TC-SCIM-v0'`` + ``'Cont-TC-SCIM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` @@ -23,7 +23,6 @@ class AbcContTorqueControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment - Motor: :py:class:`.SquirrelCageInductionMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -78,7 +77,7 @@ class AbcContTorqueControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-TC-SCIM-v0', + ... 'Cont-TC-SCIM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -90,7 +89,7 @@ class AbcContTorqueControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, state_action_processors=()): @@ -101,7 +100,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -137,10 +135,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.SquirrelCageInductionMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='abc' ) reference_generator = initialize( ReferenceGenerator, diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_cc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_cc_scim_env.py deleted file mode 100644 index 23767120..00000000 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_cc_scim_env.py +++ /dev/null @@ -1,169 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SquirrelCageInductionMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContCurrentControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain cont. control set current controlled squirrel cage induction motor. - - Key: - ``'DqCont-CC-SCIM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.SquirrelCageInductionMotor` - - Load: :py:class:`.ConstantSpeedLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'i_sd' = 0.5, 'i_sq' = 0.5`` - - - Visualization: :py:class:`.MotorDashboard` current and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``[ - 'omega' , 'torque', - 'i_sa', 'i_sb', 'i_sc', 'i_sd', 'i_sq', - 'u_sa', 'u_sb', 'u_sc', 'u_sd', 'u_sq', - 'epsilon', 'u_sup' - ]`` - - Reference Variables: - ``['i_sd', 'i_sq']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=14 * [-1], high=14 * [1]) - - Reference Space: - Box(low=[-1, -1], high=[1, 1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='i_sq', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'DqCont-CC-SCIM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the - actions to and states from the physical system before they are used in the environment. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - default_subgenerators = ( - WienerProcessReferenceGenerator(reference_state='i_sd'), - WienerProcessReferenceGenerator(reference_state='i_sq') - ) - - physical_system = SquirrelCageInductionMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.SquirrelCageInductionMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq' - ) - reference_generator = initialize( - ReferenceGenerator, - reference_generator, - MultipleReferenceGenerator, - dict(sub_generators=default_subgenerators) - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) - ) - visualization = initialize( - ElectricMotorVisualization, - visualization, - MotorDashboard, - dict(state_plots=('i_sd', 'i_sq'), action_plots='all') - ) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors - ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_sc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_sc_scim_env.py deleted file mode 100644 index a496fd0b..00000000 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_sc_scim_env.py +++ /dev/null @@ -1,161 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SquirrelCageInductionMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContSpeedControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain cont. control set speed controlled squirrel cage induction motor. - - Key: - ``'DqCont-SC-SCIM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.SquirrelCageInductionMotor` - - Load: :py:class:`.PolynomialStaticLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'omega' = 1.0`` - - - Visualization: :py:class:`.MotorDashboard` current and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``[ - 'omega' , 'torque', - 'i_sa', 'i_sb', 'i_sc', 'i_sd', 'i_sq', - 'u_sa', 'u_sb', 'u_sc', 'u_sd', 'u_sq', - 'epsilon', 'u_sup' - ]`` - - Reference Variables: - ``['omega']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=14 * [-1], high=14 * [1]) - - Reference Space: - Box(low=[-1, -1], high=[1, 1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='i_sq', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'DqCont-SC-SCIM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the - actions to and states from the physical system before they are used in the environment. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - - physical_system = SquirrelCageInductionMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.SquirrelCageInductionMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict( - load_parameter=dict(a=0.01, b=0.01, c=0.0) - )), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq', - ) - reference_generator = initialize( - ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, - dict(reference_state='omega', sigma_range=(1e-3, 1e-2)), - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(omega=1.0)) - ) - visualization = initialize( - ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors - ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_tc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_tc_scim_env.py deleted file mode 100644 index 4da1127e..00000000 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/dqcont_tc_scim_env.py +++ /dev/null @@ -1,164 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SquirrelCageInductionMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContTorqueControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain cont. control set torque controlled squirrel cage induction motor. - - Key: - ``'DqCont-TC-SCIM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.SquirrelCageInductionMotor` - - Load: :py:class:`.ConstantSpeedLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'torque' = 1.0`` - - - Visualization: :py:class:`.MotorDashboard` torque and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``[ - 'omega' , 'torque', - 'i_sa', 'i_sb', 'i_sc', 'i_sd', 'i_sq', - 'u_sa', 'u_sb', 'u_sc', 'u_sd', 'u_sq', - 'epsilon', 'u_sup' - ]`` - - Reference Variables: - ``['torque']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=14 * [-1], high=14 * [1]) - - Reference Space: - Box(low=[-1], high=[1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='i_sq', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'DqCont-TC-SCIM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the - actions to and states from the physical system before they are used in the environment. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - physical_system = SquirrelCageInductionMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.SquirrelCageInductionMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq' - ) - reference_generator = initialize( - ReferenceGenerator, - reference_generator, - WienerProcessReferenceGenerator, - dict(reference_state='torque') - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(torque=1.0)) - ) - visualization = initialize( - ElectricMotorVisualization, - visualization, - MotorDashboard, - dict(state_plots=('torque',), action_plots='all') - ) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors - ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_cc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_cc_scim_env.py index cbec2251..574fc5af 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_cc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_cc_scim_env.py @@ -23,7 +23,6 @@ class FiniteCurrentControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment - Motor: :py:class:`.SquirrelCageInductionMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` @@ -90,7 +89,7 @@ class FiniteCurrentControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, state_action_processors=()): @@ -101,7 +100,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -131,7 +129,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve This class is then initialized with its default parameters. The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ - default_subgenerators = ( + default_sub_generators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), WienerProcessReferenceGenerator(reference_state='i_sq') ) @@ -142,7 +140,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.SquirrelCageInductionMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) @@ -150,7 +147,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ReferenceGenerator, reference_generator, MultipleReferenceGenerator, - dict(sub_generators=default_subgenerators) + dict(sub_generators=default_sub_generators) ) reward_function = initialize( RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_sc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_sc_scim_env.py index daa1ab8c..12b39990 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_sc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_sc_scim_env.py @@ -23,7 +23,6 @@ class FiniteSpeedControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.SquirrelCageInductionMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -90,7 +89,7 @@ class FiniteSpeedControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, state_action_processors=()): @@ -101,7 +100,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -140,7 +138,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.01, b=0.01, c=0.0) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_tc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_tc_scim_env.py index d204370d..3f0438bd 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_tc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_tc_scim_env.py @@ -23,7 +23,6 @@ class FiniteTorqueControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment) - Motor: :py:class:`.SquirrelCageInductionMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -90,7 +89,7 @@ class FiniteTorqueControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment) >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, state_action_processors=()): @@ -101,7 +100,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -137,7 +135,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.SquirrelCageInductionMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_pmsm/__init__.py b/gym_electric_motor/envs/gym_pmsm/__init__.py index 25b259bc..37930dfc 100644 --- a/gym_electric_motor/envs/gym_pmsm/__init__.py +++ b/gym_electric_motor/envs/gym_pmsm/__init__.py @@ -1,9 +1,6 @@ -from .dqcont_cc_pmsm_env import DqContCurrentControlPermanentMagnetSynchronousMotorEnv -from .dqcont_tc_pmsm_env import DqContTorqueControlPermanentMagnetSynchronousMotorEnv -from .dqcont_sc_pmsm_env import DqContSpeedControlPermanentMagnetSynchronousMotorEnv -from .abccont_sc_pmsm_env import AbcContSpeedControlPermanentMagnetSynchronousMotorEnv -from .abccont_tc_pmsm_env import AbcContTorqueControlPermanentMagnetSynchronousMotorEnv -from .abccont_cc_pmsm_env import AbcContCurrentControlPermanentMagnetSynchronousMotorEnv +from .cont_sc_pmsm_env import ContSpeedControlPermanentMagnetSynchronousMotorEnv +from .cont_tc_pmsm_env import ContTorqueControlPermanentMagnetSynchronousMotorEnv +from .cont_cc_pmsm_env import ContCurrentControlPermanentMagnetSynchronousMotorEnv from .finite_tc_pmsm_env import FiniteTorqueControlPermanentMagnetSynchronousMotorEnv from .finite_sc_pmsm_env import FiniteSpeedControlPermanentMagnetSynchronousMotorEnv from .finite_cc_pmsm_env import FiniteCurrentControlPermanentMagnetSynchronousMotorEnv diff --git a/gym_electric_motor/envs/gym_pmsm/abccont_cc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/cont_cc_pmsm_env.py similarity index 94% rename from gym_electric_motor/envs/gym_pmsm/abccont_cc_pmsm_env.py rename to gym_electric_motor/envs/gym_pmsm/cont_cc_pmsm_env.py index d2a05ec1..32c08082 100644 --- a/gym_electric_motor/envs/gym_pmsm/abccont_cc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/cont_cc_pmsm_env.py @@ -9,13 +9,13 @@ from gym_electric_motor.constraints import SquaredConstraint -class AbcContCurrentControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): +class ContCurrentControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): """ Description: Environment to simulate a abc-domain continuous control set current controlled permanent magnet synchr. motor. Key: - ``'AbcCont-CC-PMSM-v0'`` + ``'Cont-CC-PMSM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` @@ -23,7 +23,6 @@ class AbcContCurrentControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvir - Motor: :py:class:`.PermanentMagnetSynchronousMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` @@ -73,7 +72,7 @@ class AbcContCurrentControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvir ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-CC-PMSM-v0', + ... 'Cont-CC-PMSM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -85,7 +84,7 @@ class AbcContCurrentControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvir >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, state_action_processors=()): @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -137,10 +135,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='abc' ) reference_generator = initialize( ReferenceGenerator, diff --git a/gym_electric_motor/envs/gym_pmsm/abccont_sc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/cont_sc_pmsm_env.py similarity index 94% rename from gym_electric_motor/envs/gym_pmsm/abccont_sc_pmsm_env.py rename to gym_electric_motor/envs/gym_pmsm/cont_sc_pmsm_env.py index 51f969ad..2da786bf 100644 --- a/gym_electric_motor/envs/gym_pmsm/abccont_sc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/cont_sc_pmsm_env.py @@ -9,13 +9,13 @@ from gym_electric_motor.constraints import SquaredConstraint -class AbcContSpeedControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): +class ContSpeedControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): """ Description: Environment to simulate a abc-domain continuous control set speed controlled permanent magnet synchr. motor. Key: - ``'AbcCont-SC-PMSM-v0'`` + ``'Cont-SC-PMSM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` @@ -23,7 +23,6 @@ class AbcContSpeedControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviron - Motor: :py:class:`.PermanentMagnetSynchronousMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -73,7 +72,7 @@ class AbcContSpeedControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviron ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-SC-PMSM-v0', + ... 'Cont-SC-PMSM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -85,7 +84,7 @@ class AbcContSpeedControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviron >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, state_action_processors=()): @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -134,10 +132,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.01, b=0.01, c=0.0) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='abc', ) reference_generator = initialize( ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, dict(reference_state='omega') diff --git a/gym_electric_motor/envs/gym_pmsm/abccont_tc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/cont_tc_pmsm_env.py similarity index 94% rename from gym_electric_motor/envs/gym_pmsm/abccont_tc_pmsm_env.py rename to gym_electric_motor/envs/gym_pmsm/cont_tc_pmsm_env.py index 4f142aed..b8e98bd2 100644 --- a/gym_electric_motor/envs/gym_pmsm/abccont_tc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/cont_tc_pmsm_env.py @@ -9,13 +9,13 @@ from gym_electric_motor.constraints import SquaredConstraint -class AbcContTorqueControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): +class ContTorqueControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): """ Description: Environment to simulate a abc-domain continuous control set torque controlled permanent magnet synchr. motor. Key: - ``'AbcCont-TC-PMSM-v0'`` + ``'Cont-TC-PMSM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` @@ -23,7 +23,6 @@ class AbcContTorqueControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviro - Motor: :py:class:`.PermanentMagnetSynchronousMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -73,7 +72,7 @@ class AbcContTorqueControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviro ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-TC-PMSM-v0', + ... 'Cont-TC-PMSM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -85,7 +84,7 @@ class AbcContTorqueControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviro >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, state_action_processors=()): @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -132,10 +130,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='abc' ) reference_generator = initialize( ReferenceGenerator, diff --git a/gym_electric_motor/envs/gym_pmsm/dqcont_cc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/dqcont_cc_pmsm_env.py deleted file mode 100644 index 8c15b195..00000000 --- a/gym_electric_motor/envs/gym_pmsm/dqcont_cc_pmsm_env.py +++ /dev/null @@ -1,164 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContCurrentControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain continuous control set current controlled permanent magnet synchr. motor. - - Key: - ``'DqCont-CC-PMSM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.PermanentMagnetSynchronousMotor` - - Load: :py:class:`.ConstantSpeedLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'i_sd' = 0.5, 'i_sq' = 0.5`` - - - Visualization: :py:class:`.MotorDashboard` current and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` - - Reference Variables: - ``['i_sd', 'i_sq']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=13 * [-1], high=13 * [1]) - - Reference Space: - Box(low=[-1, -1], high=[1, 1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='i_sq', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'DqCont-CC-PMSM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the - actions to and states from the physical system before they are used in the environment. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - default_subgenerators = ( - WienerProcessReferenceGenerator(reference_state='i_sd'), - WienerProcessReferenceGenerator(reference_state='i_sq') - ) - - physical_system = SynchronousMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq' - ) - reference_generator = initialize( - ReferenceGenerator, - reference_generator, - MultipleReferenceGenerator, - dict(sub_generators=default_subgenerators) - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) - ) - visualization = initialize( - ElectricMotorVisualization, - visualization, - MotorDashboard, - dict(state_plots=('i_sd', 'i_sq'), action_plots='all') - ) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors - ) diff --git a/gym_electric_motor/envs/gym_pmsm/dqcont_sc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/dqcont_sc_pmsm_env.py deleted file mode 100644 index 030c3d03..00000000 --- a/gym_electric_motor/envs/gym_pmsm/dqcont_sc_pmsm_env.py +++ /dev/null @@ -1,154 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContSpeedControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain continuous control set speed controlled permanent magnet synchr. motor. - - Key: - ``'DqCont-SC-PMSM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.PermanentMagnetSynchronousMotor` - - Load: :py:class:`.PolynomialStaticLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'omega' = 1.0`` - - - Visualization: :py:class:`.MotorDashboard` current and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` - - Reference Variables: - ``['omega']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=13 * [-1], high=13 * [1]) - - Reference Space: - Box(low=[-1, -1], high=[1, 1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='omega', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'DqCont-SC-PMSM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the - actions to and states from the physical system before they are used in the environment. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - physical_system = SynchronousMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict( - load_parameter=dict(a=0.01, b=0.01, c=0.0) - )), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq', - ) - reference_generator = initialize( - ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, dict(reference_state='omega') - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(omega=1.0)) - ) - visualization = initialize( - ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors - ) diff --git a/gym_electric_motor/envs/gym_pmsm/dqcont_tc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/dqcont_tc_pmsm_env.py deleted file mode 100644 index 630f27f9..00000000 --- a/gym_electric_motor/envs/gym_pmsm/dqcont_tc_pmsm_env.py +++ /dev/null @@ -1,159 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContTorqueControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain continuous control set torque controlled permanent magnet synchr. motor. - - Key: - ``'DqCont-TC-PMSM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.PermanentMagnetSynchronousMotor` - - Load: :py:class:`.ConstantSpeedLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'torque' = 1.0`` - - - Visualization: :py:class:`.MotorDashboard` torque and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` - - Reference Variables: - ``['torque']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=13 * [-1], high=13 * [1]) - - Reference Space: - Box(low=[-1], high=[1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='torque', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'DqCont-TC-PMSM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the - actions to and states from the physical system before they are used in the environment. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - physical_system = SynchronousMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq' - ) - reference_generator = initialize( - ReferenceGenerator, - reference_generator, - WienerProcessReferenceGenerator, - dict(reference_state='torque') - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(torque=1.0)) - ) - visualization = initialize( - ElectricMotorVisualization, - visualization, - MotorDashboard, - dict(state_plots=('torque',), action_plots='all') - ) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors - ) diff --git a/gym_electric_motor/envs/gym_pmsm/finite_cc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/finite_cc_pmsm_env.py index f1bcc47a..00c85cee 100644 --- a/gym_electric_motor/envs/gym_pmsm/finite_cc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/finite_cc_pmsm_env.py @@ -23,7 +23,6 @@ class FiniteCurrentControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviro - Motor: :py:class:`.PermanentMagnetSynchronousMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` @@ -85,7 +84,7 @@ class FiniteCurrentControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviro >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, state_action_processors=()): @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -126,7 +124,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve This class is then initialized with its default parameters. The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ - default_subgenerators = ( + default_sub_generators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), WienerProcessReferenceGenerator(reference_state='i_sq') ) @@ -137,7 +135,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) @@ -145,7 +142,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ReferenceGenerator, reference_generator, MultipleReferenceGenerator, - dict(sub_generators=default_subgenerators) + dict(sub_generators=default_sub_generators) ) reward_function = initialize( RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) diff --git a/gym_electric_motor/envs/gym_pmsm/finite_sc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/finite_sc_pmsm_env.py index 66f1d08b..bd8cb527 100644 --- a/gym_electric_motor/envs/gym_pmsm/finite_sc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/finite_sc_pmsm_env.py @@ -23,7 +23,6 @@ class FiniteSpeedControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironm - Motor: :py:class:`.PermanentMagnetSynchronousMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -85,7 +84,7 @@ class FiniteSpeedControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironm >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, state_action_processors=()): @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -134,7 +132,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.01, b=0.01, c=0.0) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_pmsm/finite_tc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/finite_tc_pmsm_env.py index 517ec1ba..f8b76f19 100644 --- a/gym_electric_motor/envs/gym_pmsm/finite_tc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/finite_tc_pmsm_env.py @@ -23,7 +23,6 @@ class FiniteTorqueControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviron - Motor: :py:class:`.PermanentMagnetSynchronousMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -85,7 +84,7 @@ class FiniteTorqueControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviron >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, state_action_processors=()): @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -132,7 +130,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_synrm/__init__.py b/gym_electric_motor/envs/gym_synrm/__init__.py index 4e3f79bb..ed631ae0 100644 --- a/gym_electric_motor/envs/gym_synrm/__init__.py +++ b/gym_electric_motor/envs/gym_synrm/__init__.py @@ -1,9 +1,6 @@ -from .dqcont_cc_synrm_env import DqContCurrentControlSynchronousReluctanceMotorEnv -from .dqcont_tc_synrm_env import DqContTorqueControlSynchronousReluctanceMotorEnv -from .dqcont_sc_synrm_env import DqContSpeedControlSynchronousReluctanceMotorEnv -from .abccont_sc_synrm_env import AbcContSpeedControlSynchronousReluctanceMotorEnv -from .abccont_tc_synrm_env import AbcContTorqueControlSynchronousReluctanceMotorEnv -from .abccont_cc_synrm_env import AbcContCurrentControlSynchronousReluctanceMotorEnv +from .cont_sc_synrm_env import ContSpeedControlSynchronousReluctanceMotorEnv +from .cont_tc_synrm_env import ContTorqueControlSynchronousReluctanceMotorEnv +from .cont_cc_synrm_env import ContCurrentControlSynchronousReluctanceMotorEnv from .finite_tc_synrm_env import FiniteTorqueControlSynchronousReluctanceMotorEnv from .finite_sc_synrm_env import FiniteSpeedControlSynchronousReluctanceMotorEnv from .finite_cc_synrm_env import FiniteCurrentControlSynchronousReluctanceMotorEnv diff --git a/gym_electric_motor/envs/gym_synrm/abccont_cc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/cont_cc_synrm_env.py similarity index 93% rename from gym_electric_motor/envs/gym_synrm/abccont_cc_synrm_env.py rename to gym_electric_motor/envs/gym_synrm/cont_cc_synrm_env.py index 58fa3245..884b3180 100644 --- a/gym_electric_motor/envs/gym_synrm/abccont_cc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/cont_cc_synrm_env.py @@ -9,13 +9,13 @@ from gym_electric_motor.constraints import SquaredConstraint -class AbcContCurrentControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): +class ContCurrentControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): """ Description: Environment to simulate a abc-domain continuous control set current controlled synchronous reluctance motor. Key: - ``'AbcCont-CC-PMSM-v0'`` + ``'Cont-CC-PMSM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` @@ -23,7 +23,6 @@ class AbcContCurrentControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironmen - Motor: :py:class:`.SynchronousReluctanceMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` @@ -73,7 +72,7 @@ class AbcContCurrentControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironmen ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-CC-SynRM-v0', + ... 'Cont-CC-SynRM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -85,7 +84,7 @@ class AbcContCurrentControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironmen >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, state_action_processors=()): @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -126,7 +124,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve This class is then initialized with its default parameters. The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ - default_subgenerators = ( + default_sub_generators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), WienerProcessReferenceGenerator(reference_state='i_sq') ) @@ -137,16 +135,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.SynchronousReluctanceMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='abc' ) reference_generator = initialize( ReferenceGenerator, reference_generator, MultipleReferenceGenerator, - dict(sub_generators=default_subgenerators) + dict(sub_generators=default_sub_generators) ) reward_function = initialize( RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) diff --git a/gym_electric_motor/envs/gym_synrm/abccont_sc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/cont_sc_synrm_env.py similarity index 94% rename from gym_electric_motor/envs/gym_synrm/abccont_sc_synrm_env.py rename to gym_electric_motor/envs/gym_synrm/cont_sc_synrm_env.py index 93ac274a..c27a6087 100644 --- a/gym_electric_motor/envs/gym_synrm/abccont_sc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/cont_sc_synrm_env.py @@ -9,13 +9,13 @@ from gym_electric_motor.constraints import SquaredConstraint -class AbcContSpeedControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): +class ContSpeedControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): """ Description: Environment to simulate a abc-domain continuous control set speed controlled synchronous reluctance motor. Key: - ``'AbcCont-SC-SynRM-v0'`` + ``'Cont-SC-SynRM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` @@ -23,7 +23,6 @@ class AbcContSpeedControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment) - Motor: :py:class:`.SynchronousReluctanceMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -73,7 +72,7 @@ class AbcContSpeedControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment) ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-SC-PMSM-v0', + ... 'Cont-SC-PMSM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -85,7 +84,7 @@ class AbcContSpeedControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment) >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, state_action_processors=()): @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -134,10 +132,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.01, b=0.01, c=0.0) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='abc', ) reference_generator = initialize( ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, diff --git a/gym_electric_motor/envs/gym_synrm/abccont_tc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/cont_tc_synrm_env.py similarity index 94% rename from gym_electric_motor/envs/gym_synrm/abccont_tc_synrm_env.py rename to gym_electric_motor/envs/gym_synrm/cont_tc_synrm_env.py index 882b4011..9f276836 100644 --- a/gym_electric_motor/envs/gym_synrm/abccont_tc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/cont_tc_synrm_env.py @@ -9,13 +9,13 @@ from gym_electric_motor.constraints import SquaredConstraint -class AbcContTorqueControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): +class ContTorqueControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): """ Description: Environment to simulate a abc-domain continuous control set torque controlled synchronous reluctance motor. Key: - ``'AbcCont-TC-SynRM-v0'`` + ``'Cont-TC-SynRM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` @@ -23,7 +23,6 @@ class AbcContTorqueControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment - Motor: :py:class:`.SynchronousReluctanceMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -73,7 +72,7 @@ class AbcContTorqueControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-TC-SynRM-v0', + ... 'Cont-TC-SynRM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -85,7 +84,7 @@ class AbcContTorqueControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, state_action_processors=()): @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -132,10 +130,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.SynchronousReluctanceMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='abc' ) reference_generator = initialize( ReferenceGenerator, diff --git a/gym_electric_motor/envs/gym_synrm/dqcont_cc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/dqcont_cc_synrm_env.py deleted file mode 100644 index 9d04a860..00000000 --- a/gym_electric_motor/envs/gym_synrm/dqcont_cc_synrm_env.py +++ /dev/null @@ -1,164 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContCurrentControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain continuous control set current controlled synchronous reluctance motor. - - Key: - ``'DqCont-CC-SynRM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.SynchronousReluctanceMotor` - - Load: :py:class:`.ConstantSpeedLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'i_sd' = 0.5, 'i_sq' = 0.5`` - - - Visualization: :py:class:`.MotorDashboard` current and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` - - Reference Variables: - ``['i_sd', 'i_sq']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=13 * [-1], high=13 * [1]) - - Reference Space: - Box(low=[-1, -1], high=[1, 1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='i_sq', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'DqCont-CC-SynRM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the - actions to and states from the physical system before they are used in the environment. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - default_subgenerators = ( - WienerProcessReferenceGenerator(reference_state='i_sd'), - WienerProcessReferenceGenerator(reference_state='i_sq') - ) - - physical_system = SynchronousMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.SynchronousReluctanceMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq' - ) - reference_generator = initialize( - ReferenceGenerator, - reference_generator, - MultipleReferenceGenerator, - dict(sub_generators=default_subgenerators) - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) - ) - visualization = initialize( - ElectricMotorVisualization, - visualization, - MotorDashboard, - dict(state_plots=('i_sd', 'i_sq'), action_plots='all') - ) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors - ) diff --git a/gym_electric_motor/envs/gym_synrm/dqcont_sc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/dqcont_sc_synrm_env.py deleted file mode 100644 index be13d868..00000000 --- a/gym_electric_motor/envs/gym_synrm/dqcont_sc_synrm_env.py +++ /dev/null @@ -1,155 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContSpeedControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain continuous control set speed controlled synchronous reluctance motor. - - Key: - ``'DqCont-SC-SynRM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.SynchronousReluctanceMotor` - - Load: :py:class:`.PolynomialStaticLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'omega' = 1.0`` - - - Visualization: :py:class:`.MotorDashboard` current and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` - - Reference Variables: - ``['omega']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=13 * [-1], high=13 * [1]) - - Reference Space: - Box(low=[-1, -1], high=[1, 1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='omega', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'DqCont-SC-SynRM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the - actions to and states from the physical system before they are used in the environment. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - physical_system = SynchronousMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.SynchronousReluctanceMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict( - load_parameter=dict(a=0.01, b=0.01, c=0.0) - )), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq', - ) - reference_generator = initialize( - ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, - dict(reference_state='omega', sigma_range=(1e-3, 1e-2)), - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(omega=1.0)) - ) - visualization = initialize( - ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors - ) diff --git a/gym_electric_motor/envs/gym_synrm/dqcont_tc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/dqcont_tc_synrm_env.py deleted file mode 100644 index d73a99ea..00000000 --- a/gym_electric_motor/envs/gym_synrm/dqcont_tc_synrm_env.py +++ /dev/null @@ -1,159 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContTorqueControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain continuous control set torque controlled synchronous reluctance motor. - - Key: - ``'DqCont-TC-SynRM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.SynchronousReluctanceMotor` - - Load: :py:class:`.ConstantSpeedLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'torque' = 1.0`` - - - Visualization: :py:class:`.MotorDashboard` torque and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` - - Reference Variables: - ``['torque']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=13 * [-1], high=13 * [1]) - - Reference Space: - Box(low=[-1], high=[1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='torque', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'DqCont-TC-SynRM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the - actions to and states from the physical system before they are used in the environment. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - physical_system = SynchronousMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.SynchronousReluctanceMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq' - ) - reference_generator = initialize( - ReferenceGenerator, - reference_generator, - WienerProcessReferenceGenerator, - dict(reference_state='torque') - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(torque=1.0)) - ) - visualization = initialize( - ElectricMotorVisualization, - visualization, - MotorDashboard, - dict(state_plots=('torque',), action_plots='all') - ) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors - ) diff --git a/gym_electric_motor/envs/gym_synrm/finite_cc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/finite_cc_synrm_env.py index eb11f2b0..a2e8898e 100644 --- a/gym_electric_motor/envs/gym_synrm/finite_cc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/finite_cc_synrm_env.py @@ -23,7 +23,6 @@ class FiniteCurrentControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment - Motor: :py:class:`.SynchronousReluctanceMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` @@ -85,7 +84,7 @@ class FiniteCurrentControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, state_action_processors=()): @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -126,7 +124,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve This class is then initialized with its default parameters. The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ - default_subgenerators = ( + default_sub_generators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), WienerProcessReferenceGenerator(reference_state='i_sq') ) @@ -137,7 +135,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.SynchronousReluctanceMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) @@ -145,7 +142,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ReferenceGenerator, reference_generator, MultipleReferenceGenerator, - dict(sub_generators=default_subgenerators) + dict(sub_generators=default_sub_generators) ) reward_function = initialize( RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) diff --git a/gym_electric_motor/envs/gym_synrm/finite_sc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/finite_sc_synrm_env.py index 5b8a3c62..60695cd4 100644 --- a/gym_electric_motor/envs/gym_synrm/finite_sc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/finite_sc_synrm_env.py @@ -23,7 +23,6 @@ class FiniteSpeedControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): - Motor: :py:class:`.SynchronousReluctanceMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -85,7 +84,7 @@ class FiniteSpeedControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, state_action_processors=()): @@ -134,7 +133,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve load_parameter=dict(a=0.01, b=0.01, c=0.0) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/envs/gym_synrm/finite_tc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/finite_tc_synrm_env.py index e6f4533e..0a4fa512 100644 --- a/gym_electric_motor/envs/gym_synrm/finite_tc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/finite_tc_synrm_env.py @@ -23,7 +23,6 @@ class FiniteTorqueControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment) - Motor: :py:class:`.SynchronousReluctanceMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -85,7 +84,7 @@ class FiniteTorqueControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment) >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, state_action_processors=()): @@ -96,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -132,7 +130,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.SynchronousReluctanceMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) diff --git a/gym_electric_motor/physical_systems/__init__.py b/gym_electric_motor/physical_systems/__init__.py index 7861559a..6b2069bf 100644 --- a/gym_electric_motor/physical_systems/__init__.py +++ b/gym_electric_motor/physical_systems/__init__.py @@ -13,8 +13,6 @@ from .solvers import OdeSolver, EulerSolver, ScipyOdeIntSolver, ScipySolveIvpSolver, ScipyOdeSolver -from .noise_generators import NoiseGenerator, GaussianWhiteNoiseGenerator - from .voltage_supplies import VoltageSupply, IdealVoltageSupply, RCVoltageSupply, AC1PhaseSupply, AC3PhaseSupply @@ -25,7 +23,6 @@ register_superclass(MechanicalLoad) register_superclass(ElectricMotor) register_superclass(OdeSolver) -register_superclass(NoiseGenerator) register_superclass(VoltageSupply) @@ -50,8 +47,6 @@ register_class(ConstantSpeedLoad, MechanicalLoad, 'ConstSpeedLoad') register_class(ExternalSpeedLoad, MechanicalLoad, 'ExtSpeedLoad') -register_class(GaussianWhiteNoiseGenerator, NoiseGenerator, 'GWN') - register_class(EulerSolver, OdeSolver, 'euler') register_class(ScipyOdeSolver, OdeSolver, 'scipy.ode') register_class(ScipySolveIvpSolver, OdeSolver, 'scipy.solve_ivp') diff --git a/gym_electric_motor/physical_systems/converters.py b/gym_electric_motor/physical_systems/converters.py index 04dbb2bd..317948ea 100644 --- a/gym_electric_motor/physical_systems/converters.py +++ b/gym_electric_motor/physical_systems/converters.py @@ -25,18 +25,15 @@ class PowerElectronicConverter: #: Default action that is taken after a reset. _reset_action = None - def __init__(self, tau, dead_time=False, interlocking_time=0.0): + def __init__(self, tau, interlocking_time=0.0): """ :param tau: Discrete time step of the system in seconds - :param dead_time: Flag, if a system dead_time of one cycle should be considered. :param interlocking_time: Interlocking time of the transistors in seconds """ self._tau = tau - self._dead_time = dead_time - self._dead_time_action = self._reset_action - self._current_action = self._reset_action self._interlocking_time = interlocking_time self._action_start_time = 0.0 + self._current_action = None def reset(self): """ @@ -45,8 +42,7 @@ def reset(self): Returns: list(float): A default output voltage after reset(=0V). """ - self._dead_time_action = self._reset_action - self._current_action = self._reset_action + self._current_action = None self._action_start_time = 0.0 return [0.0] @@ -61,13 +57,9 @@ def set_action(self, action, t): Returns: list(float): Times when a switching action occurs and the conversion function must be called by the system. """ - if self._dead_time: - self._current_action = self._dead_time_action - self._dead_time_action = action - else: - self._current_action = action self._action_start_time = t - return self._set_switching_pattern() + self._current_action = action + return self._set_switching_pattern(action) def i_sup(self, i_out): """ @@ -97,7 +89,7 @@ def convert(self, i_out, t): """ raise NotImplementedError - def _set_switching_pattern(self): + def _set_switching_pattern(self, action): """ Method to calculate the switching pattern and corresponding switching times for the next time step. At least, the next time step [t + tau] is returned. @@ -105,7 +97,7 @@ def _set_switching_pattern(self): Returns: list(float): Switching times. """ - self._switching_pattern = [self._current_action] + self._switching_pattern = [action] return [self._action_start_time + self._tau] @@ -284,18 +276,18 @@ def i_sup(self, i_out): else: raise Exception('Invalid switching state of the converter') - def _set_switching_pattern(self): + def _set_switching_pattern(self, action): # Docstring in base class if ( - self._current_action == 0 + action == 0 or self._switching_state == 0 - or self._current_action == self._switching_state + or action == self._switching_state or self._interlocking_time == 0 ): - self._switching_pattern = [self._current_action] + self._switching_pattern = [action] return [self._action_start_time + self._tau] else: - self._switching_pattern = [0, self._current_action] + self._switching_pattern = [0, action] return [self._action_start_time + self._interlocking_time, self._action_start_time + self._tau] @@ -404,7 +396,6 @@ class ContTwoQuadrantConverter(ContDynamicallyAveragedConverter): currents = Box(-1, 1, shape=(1,), dtype=np.float64) action_space = Box(0, 1, shape=(1,), dtype=np.float64) - def _convert(self, *_): # Docstring in base class return self._current_action[0] @@ -580,8 +571,8 @@ def i_sup(self, i_out): class ContMultiConverter(ContDynamicallyAveragedConverter): """ - Converter that allows to include an arbitrary number of independent continuous subconverters. - Subconverters must be 'elementary' and can not be MultiConverters. + Converter that allows to include an arbitrary number of independent continuous sub-converters. + Sub-converters must be 'elementary' and can not be MultiConverters. Key: 'Cont-Multi' @@ -605,7 +596,9 @@ def __init__(self, subconverters, **kwargs): kwargs(dict): Parameters to pass to the Subconverters """ super().__init__(**kwargs) - self._subconverters = [instantiate(PowerElectronicConverter, subconverter, **kwargs) for subconverter in subconverters] + self._sub_converters = [ + instantiate(PowerElectronicConverter, subconverter, **kwargs) for subconverter in subconverters + ] self.subsignal_current_space_dims = [] self.subsignal_voltage_space_dims = [] @@ -617,7 +610,7 @@ def __init__(self, subconverters, **kwargs): voltages_high = [] # get the limits and space dims from each subconverter - for subconverter in self._subconverters: + for subconverter in self._sub_converters: self.subsignal_current_space_dims.append(np.squeeze(subconverter.currents.shape) or 1) self.subsignal_voltage_space_dims.append(np.squeeze(subconverter.voltages.shape) or 1) @@ -652,7 +645,7 @@ def set_action(self, action, t): # Docstring in base class times = [] ind = 0 - for subconverter in self._subconverters: + for subconverter in self._sub_converters: sub_action = action[ind:ind + subconverter.action_space.shape[0]] ind += subconverter.action_space.shape[0] times += subconverter.set_action(sub_action, t) @@ -661,7 +654,7 @@ def set_action(self, action, t): def reset(self): # Docstring in base class u_in = [] - for subconverter in self._subconverters: + for subconverter in self._sub_converters: u_in += subconverter.reset() return u_in @@ -669,7 +662,7 @@ def convert(self, i_out, t): # Docstring in base class u_in = [] subsignal_idx_low = 0 - for subconverter, subsignal_space_size in zip(self._subconverters, self.subsignal_voltage_space_dims): + for subconverter, subsignal_space_size in zip(self._sub_converters, self.subsignal_voltage_space_dims): subsignal_idx_high = subsignal_idx_low + subsignal_space_size u_in += subconverter.convert(i_out[subsignal_idx_low:subsignal_idx_high], t) subsignal_idx_low = subsignal_idx_high @@ -683,7 +676,7 @@ def i_sup(self, i_out): # Docstring in base class i_sup = 0 subsignal_idx_low = 0 - for subconverter, subsignal_space_size in zip(self._subconverters, self.subsignal_current_space_dims): + for subconverter, subsignal_space_size in zip(self._sub_converters, self.subsignal_current_space_dims): subsignal_idx_high = subsignal_idx_low + subsignal_space_size i_sup += subconverter.i_sup(i_out[subsignal_idx_low:subsignal_idx_high]) subsignal_idx_low = subsignal_idx_high diff --git a/gym_electric_motor/physical_systems/noise_generators.py b/gym_electric_motor/physical_systems/noise_generators.py deleted file mode 100644 index e569a4d5..00000000 --- a/gym_electric_motor/physical_systems/noise_generators.py +++ /dev/null @@ -1,94 +0,0 @@ -import numpy as np -import warnings - -from ..random_component import RandomComponent -from ..utils import set_state_array - - -class NoiseGenerator(RandomComponent): - """ - The noise generator generates noise added to the state for the observation. - """ - - _state_variables = None - _signal_powers = None - - def reset(self): - """ - Reset of all internal states of the Noise generator to a default. - - Returns: - Noise for the initial observation at time 0. - """ - self.next_generator() - return self.noise() - - def noise(self, *_, **__): - """ - Call this function to get the additive noise. - - Returns: - ndarray(float): Array of noise added to each state. - """ - return np.zeros_like(self._state_variables, dtype=float) - - def set_state_names(self, state_names): - """ - Announcement of the state names by the physical system to the noise generator. - - Args: - state_names(list(str)): List of state names of the physical system. - """ - self._state_variables = state_names - - def set_signal_power_level(self, signal_powers): - """ - Setting of the signal power levels (Nominal values) as baseline for the noise power. - - Args: - signal_powers(list(float)): Array of signal powers (nominal values). - """ - if self._state_variables is None: - warnings.warn(UserWarning("No state variable set")) - self._signal_powers = set_state_array(signal_powers, self._state_variables) - - -class GaussianWhiteNoiseGenerator(NoiseGenerator): - """ - Noise Generator that adds gaussian distributed additional noise with zero mean and selectable power. - """ - _noise = None - - def __init__(self, noise_levels=0.0, noise_length=10000): - """ - Args: - noise_levels(dict/list/ndarray(float)): Fraction of noise power over the signal powers. - noise_length(float): Length of simultaneously generated noise points for speed up. - """ - RandomComponent.__init__(self) - self._noise_levels = noise_levels - self._noise_length = noise_length - self._noise_pointer = noise_length - - def set_state_names(self, state_names): - # Docstring of superclass - super().set_state_names(state_names) - self._noise_levels = set_state_array(self._noise_levels, state_names) - - def noise(self, *_, **__): - # Docstring of superclass - if self._noise_pointer == self._noise_length: - self._generate_noise() - noise = self._noise[self._noise_pointer] - self._noise_pointer += 1 - return noise - - def _generate_noise(self): - """ - Helper function to generate noise in batches of self._noise_length steps to avoid calculating every steps and - to speed up the computation. - """ - self._noise = self._random_generator.normal( - 0, self._noise_levels * self._signal_powers, (self._noise_length, len(self._signal_powers)) - ) - self._noise_pointer = 0 diff --git a/gym_electric_motor/physical_systems/physical_systems.py b/gym_electric_motor/physical_systems/physical_systems.py index 9eed4df0..1154453a 100644 --- a/gym_electric_motor/physical_systems/physical_systems.py +++ b/gym_electric_motor/physical_systems/physical_systems.py @@ -11,8 +11,7 @@ class SCMLSystem(PhysicalSystem, RandomComponent): """ The SCML(Supply-Converter-Motor-Load)-System is used for the simulation of - a technical setting consisting of these components as well as a noise - generator and a solver for the electrical ODE of the motor and mechanical + a technical setting consisting of these components and a solver for the electrical ODE of the motor and mechanical ODE of the load. """ OMEGA_IDX = 0 @@ -49,7 +48,7 @@ def mechanical_load(self): """The mechanical load instance in the system""" return self._mechanical_load - def __init__(self, converter, motor, load, supply, ode_solver, noise_generator=None, tau=1e-4, calc_jacobian=None): + def __init__(self, converter, motor, load, supply, ode_solver, tau=1e-4, calc_jacobian=None): """ Args: converter(PowerElectronicConverter): Converter for the physical system @@ -57,7 +56,6 @@ def __init__(self, converter, motor, load, supply, ode_solver, noise_generator=N load(MechanicalLoad): Mechanical Load of the System supply(VoltageSupply): Voltage Supply ode_solver(OdeSolver): Ode Solver to use in this setting - noise_generator(NoiseGenerator): Noise generator tau(float): discrete time step of the system calc_jacobian(bool): If True, the jacobian matrices will be taken into account for the ode-solvers. Default: The jacobians are used, if available @@ -67,9 +65,7 @@ def __init__(self, converter, motor, load, supply, ode_solver, noise_generator=N self._electrical_motor = motor self._mechanical_load = load self._supply = supply - self._noise_generator = noise_generator state_names = self._build_state_names() - self._noise_generator.set_state_names(state_names) self._ode_solver = ode_solver if calc_jacobian is None: calc_jacobian = self._electrical_motor.HAS_JACOBIAN and self._mechanical_load.HAS_JACOBIAN @@ -90,14 +86,12 @@ def __init__(self, converter, motor, load, supply, ode_solver, noise_generator=N self._nominal_state = np.zeros_like(state_names, dtype=float) self._set_limits() self._set_nominal_state() - self._noise_generator.set_signal_power_level(self._nominal_state) self.system_state = np.zeros_like(state_names, dtype=float) self._system_eq_placeholder = None self._motor_deriv_size = None self._load_deriv_size = None self._components = [ - self._supply, self._converter, self._electrical_motor, self._mechanical_load, self._ode_solver, - self._noise_generator + self._supply, self._converter, self._electrical_motor, self._mechanical_load, self._ode_solver ] def _set_limits(self): @@ -187,7 +181,6 @@ def simulate(self, action, *_, **__): self._t = self._ode_solver.t self._k += 1 torque = self._electrical_motor.torque(ode_state[self._motor_ode_idx]) - noise = self._noise_generator.noise() n_mech_states = len(self.mechanical_load.state_names) motor_state = ode_state[n_mech_states:] @@ -197,7 +190,7 @@ def simulate(self, action, *_, **__): motor_state[self._electrical_motor.CURRENTS_IDX] self.system_state[self.VOLTAGES_IDX] = u_in self.system_state[self.U_SUP_IDX] = u_sup - return (self.system_state + noise) / self._limits + return self.system_state / self._limits def _system_equation(self, t, state, u_in, **__): """ @@ -275,7 +268,6 @@ def reset(self, *_): u_in = self.converter.reset() u_in = [u * u_s for u in u_in for u_s in u_sup] torque = self.electrical_motor.torque(motor_state) - noise = self._noise_generator.reset() self._t = 0 self._k = 0 self._ode_solver.set_initial_value(ode_state, self._t) @@ -286,7 +278,7 @@ def reset(self, *_): u_in, u_sup )) - return (system_state + noise) / self._limits + return system_state / self._limits class DcMotorSystem(SCMLSystem): @@ -503,7 +495,6 @@ def simulate(self, action, *_, **__): self._t = self._ode_solver.t self._k += 1 torque = self._electrical_motor.torque(ode_state[self._motor_ode_idx]) - noise = self._noise_generator.noise() mechanical_state = ode_state[self._load_ode_idx] i_dq = ode_state[self._ode_currents_idx] i_abc = list( @@ -521,7 +512,7 @@ def simulate(self, action, *_, **__): [eps], u_sup )) - return (system_state + noise) / self._limits + return system_state / self._limits def reset(self, *_): # Docstring of superclass @@ -543,7 +534,6 @@ def reset(self, *_): i_dq = ode_state[self._ode_currents_idx] i_abc = self.dq_to_abc_space(i_dq, eps) torque = self.electrical_motor.torque(motor_state) - noise = self._noise_generator.reset() self._t = 0 self._k = 0 self._ode_solver.set_initial_value(ode_state, self._t) @@ -555,7 +545,7 @@ def reset(self, *_): [eps], u_sup, )) - return (system_state + noise) / self._limits + return system_state/ self._limits class SquirrelCageInductionMotorSystem(ThreePhaseMotorSystem): @@ -651,7 +641,6 @@ def simulate(self, action, *_, **__): self._t = self._ode_solver.t self._k += 1 torque = self._electrical_motor.torque(ode_state[self._motor_ode_idx]) - noise = self._noise_generator.noise() mechanical_state = ode_state[self._load_ode_idx] i_dq = self.alphabeta_to_dq_space(ode_state[self._ode_currents_idx], eps_fs) i_abc = list(self.dq_to_abc_space(i_dq, eps_fs)) @@ -667,7 +656,7 @@ def simulate(self, action, *_, **__): [eps], u_sup )) - return (system_state + noise) / self._limits + return system_state / self._limits def reset(self, *_): # Docstring of superclass @@ -694,7 +683,6 @@ def reset(self, *_): i_dq = self.alphabeta_to_dq_space(ode_state[self._ode_currents_idx], eps_fs) i_abc = self.dq_to_abc_space(i_dq, eps_fs) torque = self.electrical_motor.torque(motor_state) - noise = self._noise_generator.reset() self._t = 0 self._k = 0 self._ode_solver.set_initial_value(ode_state, self._t) @@ -705,23 +693,20 @@ def reset(self, *_): [eps], u_sup ]) - return (system_state + noise) / self._limits + return system_state / self._limits class DoublyFedInductionMotorSystem(ThreePhaseMotorSystem): """ SCML-System for the Doubly Fed Induction Motor """ - def __init__(self, control_space='abc', ode_solver='scipy.ode', **kwargs): + def __init__(self, ode_solver='scipy.ode', **kwargs): """ Args: control_space(str):('abc' or 'dq') Choose, if actions the actions space is in dq or abc space kwargs: Further arguments to pass tp SCMLSystem """ super().__init__(ode_solver=ode_solver, **kwargs) - self.control_space = control_space - if control_space == 'dq': - self._action_space = Box(-1, 1, shape=(4,), dtype=np.float64) self.stator_voltage_space_idx = 0 self.stator_voltage_low_idx = 0 @@ -826,16 +811,6 @@ def simulate(self, action, *_, **__): eps_field = self.calculate_field_angle(ode_state) eps_el = ode_state[self._ode_epsilon_idx] - # convert dq input voltage to abc - if self.control_space == 'dq': - stator_input_len = len(self._electrical_motor.STATOR_VOLTAGES) - rotor_input_len = len(self._electrical_motor.ROTOR_VOLTAGES) - action_stator = action[:stator_input_len] - action_rotor = action[stator_input_len:stator_input_len + rotor_input_len] - action_stator = self.dq_to_abc_space(action_stator, eps_field) - action_rotor = self.dq_to_abc_space(action_rotor, eps_field-eps_el) - action = np.concatenate((action_stator, action_rotor)).tolist() - i_sabc = self.alphabeta_to_abc_space(self._electrical_motor.i_in(ode_state[self._ode_currents_idx])) i_rdef = self.alphabeta_to_abc_space(self.calculate_rotor_current(ode_state)) switching_times = self._converter.set_action(action, self._t) @@ -877,7 +852,6 @@ def simulate(self, action, *_, **__): self._t = self._ode_solver.t self._k += 1 torque = self._electrical_motor.torque(ode_state[self._motor_ode_idx]) - noise = self._noise_generator.noise() mechanical_state = ode_state[self._load_ode_idx] i_sdq = self.alphabeta_to_dq_space(ode_state[self._ode_currents_idx], eps_field) @@ -900,7 +874,7 @@ def simulate(self, action, *_, **__): [eps_el], u_sup, )) - return (system_state + noise) / self._limits + return system_state / self._limits def reset(self, *_): # Docstring of superclass @@ -938,7 +912,6 @@ def reset(self, *_): i_rdef = self.dq_to_abc_space(i_rdq, eps_field-eps_el) torque = self.electrical_motor.torque(motor_state) - noise = self._noise_generator.reset() self._t = 0 self._k = 0 self._ode_solver.set_initial_value(ode_state, self._t) @@ -951,4 +924,4 @@ def reset(self, *_): [eps_el], u_sup ]) - return (system_state + noise) / self._limits + return system_state / self._limits diff --git a/gym_electric_motor/state_action_processors/flux_observer.py b/gym_electric_motor/state_action_processors/flux_observer.py index 62306229..7402b7f3 100644 --- a/gym_electric_motor/state_action_processors/flux_observer.py +++ b/gym_electric_motor/state_action_processors/flux_observer.py @@ -45,7 +45,6 @@ def __init__(self, current_names=('i_sa', 'i_sb', 'i_sc'), physical_system=None) self._current_names = current_names super(FluxObserver, self).__init__(physical_system) - @staticmethod def _abc_to_alphabeta_transformation(i_s): return gem.physical_systems.electric_motors.ThreePhaseMotor.t_23(i_s) diff --git a/tests/integration_tests/test_ac_environment_execution.py b/tests/integration_tests/test_ac_environment_execution.py deleted file mode 100644 index 6191f6fb..00000000 --- a/tests/integration_tests/test_ac_environment_execution.py +++ /dev/null @@ -1,36 +0,0 @@ -import pytest -import numpy as np -import gym_electric_motor as gem - -control_tasks = ['TC', 'SC', 'CC'] -action_types = ['AbcCont', 'Finite', 'DqCont'] -ac_motors = ['PMSM', 'SynRM', 'SCIM', 'DFIM'] -versions = ['v0'] - - -@pytest.mark.parametrize('no_of_steps', [100]) -@pytest.mark.parametrize('version', versions) -@pytest.mark.parametrize('ac_motor', ac_motors) -@pytest.mark.parametrize('control_task', control_tasks) -@pytest.mark.parametrize('action_type', action_types) -def test_execution(ac_motor, control_task, action_type, version, no_of_steps): - env_id = f'{action_type}-{control_task}-{ac_motor}-{version}' - env = gem.make(env_id) - done = True - for i in range(no_of_steps): - if done: - observation = env.reset() - action = env.action_space.sample() - observation, reward, done, info = env.step(action) - - assert info == {} - assert type(reward) in [float, np.float64, np.float32], 'The Reward is not a scalar floating point value.' - # Only the shape is monitored here. The states and references may lay slightly outside of the specified space. - # This happens if limits are violated or if some states are not observed to lay within their limits. - assert observation[0].shape == env.observation_space[0].shape, 'The shape of the state is incorrect.' - assert observation[1].shape == env.observation_space[1].shape, 'The shape of the reference is incorrect.' - assert not np.any(np.isnan(observation[0])), 'An invalid nan-value is in the state.' - assert not np.any(np.isnan(observation[1])), 'An invalid nan-value is in the reference.' - assert info == {} - assert type(reward) in [float, np.float64, np.float32], 'The Reward is not a scalar floating point value.' - assert not np.isnan(reward), 'Invalid nan-value as reward.' diff --git a/tests/integration_tests/test_ac_environment_seeding.py b/tests/integration_tests/test_ac_environment_seeding.py deleted file mode 100644 index 4326695f..00000000 --- a/tests/integration_tests/test_ac_environment_seeding.py +++ /dev/null @@ -1,123 +0,0 @@ -import pytest -import gym_electric_motor as gem -import numpy as np - -control_tasks = ['TC', 'SC', 'CC'] -action_types = ['AbcCont', 'DqCont', 'Finite'] -ac_motors = ['PMSM', 'SynRM', 'SCIM', 'DFIM'] -versions = ['v0'] -seeds = [123, 456, 789] - - -@pytest.mark.parametrize('no_of_steps', [100]) -@pytest.mark.parametrize('version', versions) -@pytest.mark.parametrize('ac_motor', ac_motors) -@pytest.mark.parametrize('control_task', control_tasks) -@pytest.mark.parametrize('action_type', action_types) -@pytest.mark.parametrize('seed', seeds) -def test_seeding_same_env(ac_motor, control_task, action_type, version, no_of_steps, seed): - """This test assures that a reseeding of the same environment leads to the same episodes again.""" - env_id = f'{action_type}-{control_task}-{ac_motor}-{version}' - env = gem.make(env_id) - # Initial seed - env.seed(seed) - # Sample some actions - actions = [env.action_space.sample() for _ in range(no_of_steps)] - done = True - # Data of the initial seed - states1 = [] - references1 = [] - rewards1 = [] - done1 = [] - # Simulate the environment with the sampled actions - for i in range(no_of_steps): - if done: - state, reference = env.reset() - (state, reference), reward, done, info = env.step(actions[i]) - rewards1.append(reward) - states1.append(state) - references1.append(reference) - done1.append(done) - - # Reseed the environment - env.seed(seed) - done = True - states2 = [] - references2 = [] - rewards2 = [] - done2 = [] - - # Execute the reseeded env with the same actions - for i in range(no_of_steps): - if done: - state, reference = env.reset() - (state, reference), reward, done, info = env.step(actions[i]) - rewards2.append(reward) - states2.append(state) - references2.append(reference) - done2.append(done) - - # Assure that the data of the initial and second seeding of the environment are equal - references1 = np.array(references1).flatten() - references2 = np.array(references2).flatten() - assert(np.all(np.array(states1) == np.array(states2))) - assert(np.all(np.array(references1).flatten() == np.array(references2).flatten())) - assert(np.all(np.array(rewards1) == np.array(rewards2))) - assert (np.all(np.array(done1) == np.array(done2))) - - -@pytest.mark.parametrize('no_of_steps', [100]) -@pytest.mark.parametrize('version', versions) -@pytest.mark.parametrize('ac_motor', ac_motors) -@pytest.mark.parametrize('control_task', control_tasks) -@pytest.mark.parametrize('action_type', action_types) -@pytest.mark.parametrize('seed', seeds) -def test_seeding_new_env(ac_motor, control_task, action_type, version, no_of_steps, seed): - """This test assures that two equal environments that are seeded with the same seed generate the same episodes.""" - env_id = f'{action_type}-{control_task}-{ac_motor}-{version}' - env = gem.make(env_id) - # Seed the first environment - env.seed(seed) - # Sample actions - actions = [env.action_space.sample() for _ in range(no_of_steps)] - done = True - # Episode data of the first environment - states1 = [] - references1 = [] - rewards1 = [] - done1 = [] - # Simulate the environment - for i in range(no_of_steps): - if done: - state, reference = env.reset() - (state, reference), reward, done, info = env.step(actions[i]) - rewards1.append(reward) - states1.append(state) - references1.append(reference) - done1.append(done) - # Create a new environment with the same id - env = gem.make(env_id) - # Seed the environment with the same seed - env.seed(seed) - done = True - states2 = [] - references2 = [] - rewards2 = [] - done2 = [] - # Execute the new environment - for i in range(no_of_steps): - if done: - state, reference = env.reset() - action = env.action_space.sample() - assert action in env.action_space - (state, reference), reward, done, info = env.step(actions[i]) - rewards2.append(reward) - states2.append(state) - references2.append(reference) - done2.append(done) - - # Assure that the episodes of both environments are equal - assert (np.all(np.array(states1) == np.array(states2))) - assert (np.all(np.array(references1).flatten() == np.array(references2).flatten())) - assert (np.all(np.array(rewards1) == np.array(rewards2))) - assert (np.all(np.array(done1) == np.array(done2))) diff --git a/tests/integration_tests/test_dc_environment_execution.py b/tests/integration_tests/test_environment_execution.py similarity index 92% rename from tests/integration_tests/test_dc_environment_execution.py rename to tests/integration_tests/test_environment_execution.py index 38e16772..c3e4d703 100644 --- a/tests/integration_tests/test_dc_environment_execution.py +++ b/tests/integration_tests/test_environment_execution.py @@ -4,13 +4,13 @@ control_tasks = ['TC', 'SC', 'CC'] action_types = ['Cont', 'Finite'] -dc_motors = ['SeriesDc', 'PermExDc', 'ExtExDc', 'ShuntDc'] +motors = ['SeriesDc', 'PermExDc', 'ExtExDc', 'ShuntDc', 'PMSM', 'SynRM', 'DFIM', 'SCIM'] versions = ['v0'] @pytest.mark.parametrize('no_of_steps', [100]) @pytest.mark.parametrize('version', versions) -@pytest.mark.parametrize('dc_motor', dc_motors) +@pytest.mark.parametrize('dc_motor', motors) @pytest.mark.parametrize('control_task', control_tasks) @pytest.mark.parametrize('action_type', action_types) def test_execution(dc_motor, control_task, action_type, version, no_of_steps): diff --git a/tests/integration_tests/test_dc_environment_seeding.py b/tests/integration_tests/test_environment_seeding.py similarity index 95% rename from tests/integration_tests/test_dc_environment_seeding.py rename to tests/integration_tests/test_environment_seeding.py index ef1f80f2..4953d0eb 100644 --- a/tests/integration_tests/test_dc_environment_seeding.py +++ b/tests/integration_tests/test_environment_seeding.py @@ -4,14 +4,14 @@ control_tasks = ['TC', 'SC', 'CC'] action_types = ['Cont', 'Finite'] -dc_motors = ['SeriesDc', 'PermExDc', 'ExtExDc', 'ShuntDc'] +motors = ['SeriesDc', 'PermExDc', 'ExtExDc', 'ShuntDc', 'PMSM', 'SynRM', 'DFIM', 'SCIM'] versions = ['v0'] seeds = [123, 456, 789] @pytest.mark.parametrize('no_of_steps', [100]) @pytest.mark.parametrize('version', versions) -@pytest.mark.parametrize('dc_motor', dc_motors) +@pytest.mark.parametrize('dc_motor', motors) @pytest.mark.parametrize('control_task', control_tasks) @pytest.mark.parametrize('action_type', action_types) @pytest.mark.parametrize('seed', seeds) @@ -66,7 +66,7 @@ def test_seeding_same_env(dc_motor, control_task, action_type, version, no_of_st @pytest.mark.parametrize('no_of_steps', [100]) @pytest.mark.parametrize('version', versions) -@pytest.mark.parametrize('dc_motor', dc_motors) +@pytest.mark.parametrize('dc_motor', motors) @pytest.mark.parametrize('control_task', control_tasks) @pytest.mark.parametrize('action_type', action_types) @pytest.mark.parametrize('seed', seeds) diff --git a/tests/test_environments/test_environments.py b/tests/test_environments/test_environments.py index c38d73b1..e30821d9 100644 --- a/tests/test_environments/test_environments.py +++ b/tests/test_environments/test_environments.py @@ -5,29 +5,18 @@ control_tasks = ['TC', 'SC', 'CC'] -ac_action_types = ['AbcCont', 'Finite', 'DqCont'] -dc_action_types = ['Cont', 'Finite'] +action_types = ['Cont', 'Finite'] ac_motors = ['PMSM', 'SynRM', 'SCIM', 'DFIM'] dc_motors = ['SeriesDc', 'ShuntDc', 'PermExDc', 'ExtExDc'] versions = ['v0'] @pytest.mark.parametrize('version', versions) -@pytest.mark.parametrize('dc_motor', dc_motors) +@pytest.mark.parametrize('motor', ac_motors + dc_motors) @pytest.mark.parametrize('control_task', control_tasks) -@pytest.mark.parametrize(['action_type', 'tau'], zip(dc_action_types, [1e-4, 1e-5])) -def test_tau_dc(dc_motor, control_task, action_type, version, tau): - env_id = f'{action_type}-{control_task}-{dc_motor}-{version}' - env = gem.make(env_id) - assert env.physical_system.tau == tau - - -@pytest.mark.parametrize('version', versions) -@pytest.mark.parametrize('ac_motor', ac_motors) -@pytest.mark.parametrize('control_task', control_tasks) -@pytest.mark.parametrize(['action_type', 'tau'], zip(ac_action_types, [1e-4, 1e-5, 1e-4])) -def test_tau_ac(ac_motor, control_task, action_type, version, tau): - env_id = f'{action_type}-{control_task}-{ac_motor}-{version}' +@pytest.mark.parametrize(['action_type', 'tau'], zip(action_types, [1e-4, 1e-5, 1e-4])) +def test_tau(motor, control_task, action_type, version, tau): + env_id = f'{action_type}-{control_task}-{motor}-{version}' env = gem.make(env_id) assert env.physical_system.tau == tau @@ -37,7 +26,7 @@ def test_tau_ac(ac_motor, control_task, action_type, version, tau): @pytest.mark.parametrize( ['control_task', 'referenced_states'], zip(control_tasks, [['torque'], ['omega'], ['i_sd', 'i_sq']]) ) -@pytest.mark.parametrize('action_type', ac_action_types) +@pytest.mark.parametrize('action_type', action_types) def test_referenced_states_ac(ac_motor, control_task, action_type, version, referenced_states): env_id = f'{action_type}-{control_task}-{ac_motor}-{version}' env = gem.make(env_id) diff --git a/tests/test_physical_systems/test_converters.py b/tests/test_physical_systems/test_converters.py index 0d03d7ea..d7eb579e 100644 --- a/tests/test_physical_systems/test_converters.py +++ b/tests/test_physical_systems/test_converters.py @@ -16,7 +16,6 @@ # define basic test parameter g_taus = [1E-3, 1E-4, 1E-5] -g_dead_times = [False, True] g_interlocking_times = np.array([0.0, 1 / 20, 1 / 3]) # define test parameter for different cases @@ -29,19 +28,14 @@ g_actions_1qc = np.array([0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0]) g_i_ins_1qc = np.array([-0.5, 0.25, 0.75, 1, -0.5, 0, 0.25, 0.35, -0.15, 0.65, 0.85]) g_test_voltages_1qc = np.array([1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0]) -g_test_voltages_1qc_dead_time = np.array([1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1]) # disc 2QC g_i_ins_2qc = [0, 0.5, -0.5, 0.5, 0.5, 0, -0.5, 0.5, 0.5, 0, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5] g_actions_2qc = [0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 1, 2, 2, 1, 2] g_times_2qc = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]) g_test_voltages_2qc = np.array([0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0]) -g_test_voltages_2qc_dead_time = np.array([0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1]) g_test_voltages_2qc_interlocking = np.array([0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0]) -g_test_voltages_2qc_dead_time_interlocking = np.array([0, 0, 1, 0, 0, - 1, 1, 1, 0, 0, - 0, 0, 1, 1, 0, - 0, 0, 0, 1, 0]) + # disc 4QC g_times_4qc = np.array( [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]) @@ -50,21 +44,13 @@ g_actions_4qc = np.array([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2, 2, 2, 0, 0, 3, 3, 3, 3, 0, 1, 2, 3, 1, 3, 3, 2, 1]) g_test_voltages_4qc = np.array( [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 1, 0, 0, -1, 1]) -g_test_voltages_4qc_dead_time = np.array( - [0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 1, 0, 0, -1]) g_test_voltages_4qc_interlocking = np.array( [0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 1, -1, -1, -1, 0, 0, 1, 1, 0, 0, 0, -1, 1, 1]) -g_test_voltages_4qc_dead_time_interlocking = np.array( - [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, -1, -1, -1, -1, -1, 0, 0, 1, 0, - 0, 0, 0, -1, 0, 0, 1, -1, -1, -1, 0, 1, 1, 1, 0, 0, 0, -1]) # combine all test voltages in one vector for each converter -g_1qc_test_voltages = [g_test_voltages_1qc, g_test_voltages_1qc_dead_time, g_test_voltages_1qc, - g_test_voltages_1qc_dead_time] -g_2qc_test_voltages = [g_test_voltages_2qc, g_test_voltages_2qc_dead_time, g_test_voltages_2qc_interlocking, - g_test_voltages_2qc_dead_time_interlocking] -g_4qc_test_voltages = [g_test_voltages_4qc, g_test_voltages_4qc_dead_time, g_test_voltages_4qc_interlocking, - g_test_voltages_4qc_dead_time_interlocking] +g_1qc_test_voltages = [g_test_voltages_1qc, g_test_voltages_1qc] +g_2qc_test_voltages = [g_test_voltages_2qc, g_test_voltages_2qc_interlocking] +g_4qc_test_voltages = [g_test_voltages_4qc, g_test_voltages_4qc_interlocking] g_disc_test_voltages = {'Finite-1QC': g_1qc_test_voltages, 'Finite-2QC': g_2qc_test_voltages, 'Finite-4QC': g_4qc_test_voltages} @@ -82,14 +68,10 @@ # region discrete converter -def discrete_converter_functions_testing(converter, action_space_n, times, - actions, - i_ins, - test_voltage_ideal, - test_voltage_dead_time, - test_voltage_interlocking_time, - test_voltage_dead_interlocking, - interlocking_time=0.0, dead_time=False): +def discrete_converter_functions_testing( + converter, action_space_n, times, actions, i_ins, test_voltage_ideal, test_voltage_interlocking_time, + interlocking_time=0.0 +): """ test of convert function of discrete converter :param converter: @@ -98,11 +80,8 @@ def discrete_converter_functions_testing(converter, action_space_n, times, :param actions: pre defined actions for testing :param i_ins: used input current for testing :param test_voltage_ideal: expected voltages in ideal behaviour - :param test_voltage_dead_time: expected voltages if dead time is considered :param test_voltage_interlocking_time: expected output voltages if interlocking time is considered - :param test_voltage_dead_interlocking: expected output voltages if interlocking and dead time are considered :param interlocking_time: used interlocking time - :param dead_time: used dead time :return: """ action_space = converter.action_space @@ -116,13 +95,7 @@ def discrete_converter_functions_testing(converter, action_space_n, times, for u in converter_voltage: assert converter.voltages.low[0] <= u <= converter.voltages.high[0], "Voltage limits violated" # test for different cases of (non) ideal behaviour - if dead_time and interlocking_time > 0: - test_voltage = test_voltage_dead_interlocking[step_counter] - # g_u_out_dead_time_interlocking[step_counter] - elif dead_time: - test_voltage = test_voltage_dead_time[step_counter] - # g_u_out_dead_time[step_counter] - elif interlocking_time > 0: + if interlocking_time > 0: test_voltage = test_voltage_interlocking_time[step_counter] # g_u_out_interlocking[step_counter] else: @@ -134,23 +107,21 @@ def discrete_converter_functions_testing(converter, action_space_n, times, @pytest.mark.parametrize("tau", g_taus) @pytest.mark.parametrize("interlocking_time", g_interlocking_times) -@pytest.mark.parametrize("dead_time", g_dead_times) @pytest.mark.parametrize("converter_type, action_space_n, actions, i_ins, test_voltages", [('Finite-1QC', 2, g_actions_1qc, g_i_ins_1qc, g_1qc_test_voltages), ('Finite-2QC', 3, g_actions_2qc, g_i_ins_2qc, g_2qc_test_voltages), ('Finite-4QC', 4, g_actions_4qc, g_i_ins_4qc, g_4qc_test_voltages)]) -def test_discrete_single_power_electronic_converter(converter_type, action_space_n, actions, i_ins, test_voltages, - dead_time, - interlocking_time, tau): +def test_discrete_single_power_electronic_converter( + converter_type, action_space_n, actions, i_ins, test_voltages, interlocking_time, tau +): """ - test the initialization of all single converters for different tau, interlocking times, dead times + test the initialization of all single converters for different tau, interlocking times, furthermore, test the other functions: reset, convert with different parameters :param converter_type: converter name (string) :param action_space_n: number of elements in the action space :param actions: pre defined actions for testing :param i_ins: used input currents for testing :param test_voltages: expected voltages for ideal and non ideal behaviour - :param dead_time: specifies if a dead time is considered or not :param interlocking_time: the used interlocking time :param tau: sampling time :return: @@ -159,15 +130,9 @@ def test_discrete_single_power_electronic_converter(converter_type, action_space converter = make_module(cv.PowerElectronicConverter, converter_type) g_times = g_times_4qc times = g_times * converter._tau - discrete_converter_functions_testing(converter, - action_space_n, - times, - actions, - i_ins, - test_voltages[0], - test_voltages[1], - test_voltages[2], - test_voltages[3]) + discrete_converter_functions_testing( + converter, action_space_n, times, actions, i_ins, test_voltages[0], test_voltages[1] + ) # define various constants for test times = g_times * tau @@ -175,7 +140,7 @@ def test_discrete_single_power_electronic_converter(converter_type, action_space # setup converter # initialize converter with given parameter converter = make_module(cv.PowerElectronicConverter, converter_type, tau=tau, - interlocking_time=interlocking_time, dead_time=dead_time) + interlocking_time=interlocking_time) assert converter.reset() == [0.0] # test if reset returns 0.0 # test the conversion function of the converter discrete_converter_functions_testing(converter, action_space_n, times, @@ -183,9 +148,8 @@ def test_discrete_single_power_electronic_converter(converter_type, action_space i_ins, test_voltages[0], test_voltages[1], - test_voltages[2], - test_voltages[3], - interlocking_time=interlocking_time, dead_time=dead_time) + interlocking_time=interlocking_time + ) @pytest.mark.parametrize("convert, convert_class", [ @@ -195,8 +159,7 @@ def test_discrete_single_power_electronic_converter(converter_type, action_space ]) @pytest.mark.parametrize("tau", g_taus) @pytest.mark.parametrize("interlocking_time", g_interlocking_times) -@pytest.mark.parametrize("dead_time", g_dead_times) -def test_discrete_single_initializations(convert, convert_class, tau, interlocking_time, dead_time): +def test_discrete_single_initializations(convert, convert_class, tau, interlocking_time): """ test of both ways of initialization lead to the same result :param convert: string name of the converter @@ -211,23 +174,21 @@ def test_discrete_single_initializations(convert, convert_class, tau, interlocki interlocking_time *= tau # initialize converters converter_1 = make_module(cv.PowerElectronicConverter, convert, tau=tau, - interlocking_time=interlocking_time, dead_time=dead_time) + interlocking_time=interlocking_time) converter_2 = convert_class( - tau=tau, interlocking_time=interlocking_time, dead_time=dead_time) - parameter = str(tau) + " " + str(dead_time) + " " + str(interlocking_time) + tau=tau, interlocking_time=interlocking_time) + parameter = str(tau) + " " + str(interlocking_time) # test if they are equal assert converter_1.reset() == converter_2.reset() assert converter_1.action_space == converter_2.action_space assert converter_1._tau == converter_2._tau == tau, "Error (tau): " + parameter - assert converter_1._dead_time == converter_2._dead_time == dead_time, "Dead time Error" assert converter_1._interlocking_time == converter_2._interlocking_time == interlocking_time, \ "Error (interlocking): " + parameter @pytest.mark.parametrize("tau", g_taus) @pytest.mark.parametrize("interlocking_time", g_interlocking_times) -@pytest.mark.parametrize("dead_time", g_dead_times) -def test_discrete_multi_converter_initializations(tau, interlocking_time, dead_time): +def test_discrete_multi_converter_initializations(tau, interlocking_time): """ tests different initializations of the converters :return: @@ -240,20 +201,18 @@ def test_discrete_multi_converter_initializations(tau, interlocking_time, dead_t for conv_2 in all_single_disc_converter: converter = make_module( cv.PowerElectronicConverter, 'Finite-Multi', tau=tau, - interlocking_time=interlocking_time, dead_time=dead_time, + interlocking_time=interlocking_time, subconverters=[conv_1, conv_2] ) # test if both converter have the same parameter - assert converter._subconverters[0]._tau == converter._subconverters[1]._tau == tau - assert converter._subconverters[0]._interlocking_time == converter._subconverters[1]._interlocking_time \ + assert converter._sub_converters[0]._tau == converter._sub_converters[1]._tau == tau + assert converter._sub_converters[0]._interlocking_time == converter._sub_converters[1]._interlocking_time \ == interlocking_time - assert converter._subconverters[0]._dead_time == converter._subconverters[1]._dead_time == dead_time @pytest.mark.parametrize("tau", g_taus) @pytest.mark.parametrize("interlocking_time", g_interlocking_times) -@pytest.mark.parametrize("dead_time", g_dead_times) -def test_discrete_multi_power_electronic_converter(tau, interlocking_time, dead_time): +def test_discrete_multi_power_electronic_converter(tau, interlocking_time): """ setup all combinations of single converters and test the convert function if no error is raised :return: @@ -266,13 +225,13 @@ def test_discrete_multi_power_electronic_converter(tau, interlocking_time, dead_ for conv_1 in all_single_disc_converter: converter = make_module( cv.PowerElectronicConverter, 'Finite-Multi', tau=tau, - interlocking_time=interlocking_time, dead_time=dead_time, + interlocking_time=interlocking_time, subconverters=[conv_0, conv_1] ) comparable_converter_0 = make_module(cv.PowerElectronicConverter, conv_0, tau=tau, - interlocking_time=interlocking_time, dead_time=dead_time) + interlocking_time=interlocking_time) comparable_converter_1 = make_module(cv.PowerElectronicConverter, conv_1, tau=tau, - interlocking_time=interlocking_time, dead_time=dead_time) + interlocking_time=interlocking_time) action_space_n = converter.action_space.nvec assert np.all( converter.reset() == @@ -309,8 +268,7 @@ def test_discrete_multi_power_electronic_converter(tau, interlocking_time, dead_ @pytest.mark.parametrize("converter_type", ['Cont-1QC', 'Cont-2QC', 'Cont-4QC']) @pytest.mark.parametrize("tau", g_taus) @pytest.mark.parametrize("interlocking_time", g_interlocking_times) -@pytest.mark.parametrize("dead_time", g_dead_times) -def test_continuous_power_electronic_converter(converter_type, tau, interlocking_time, dead_time): +def test_continuous_power_electronic_converter(converter_type, tau, interlocking_time): """ test the functions and especially the conversion of continuous single converter :param converter_type: @@ -319,7 +277,7 @@ def test_continuous_power_electronic_converter(converter_type, tau, interlocking interlocking_time *= tau # setup converter converter = make_module(cv.PowerElectronicConverter, converter_type, tau=tau, - interlocking_time=interlocking_time, dead_time=dead_time) + interlocking_time=interlocking_time) assert converter.reset() == [0.0], "Error reset function" action_space = converter.action_space # take 100 random actions @@ -327,16 +285,15 @@ def test_continuous_power_electronic_converter(converter_type, tau, interlocking actions = [[uniform(action_space.low, action_space.high)] for _ in range(len(g_times_cont))] times = g_times_cont * tau - continuous_converter_functions_testing(converter, times, interlocking_time, dead_time, actions, converter_type) + continuous_converter_functions_testing(converter, times, interlocking_time, actions, converter_type) -def continuous_converter_functions_testing(converter, times, interlocking_time, dead_time, actions, converter_type): +def continuous_converter_functions_testing(converter, times, interlocking_time, actions, converter_type): """ test function for conversion :param converter: instantiated converter :param times: times for set actions :param interlocking_time: used interlocking time - :param dead_time: used dead time :param actions: random actions :param converter_type: converter name (string) :return: @@ -345,7 +302,7 @@ def continuous_converter_functions_testing(converter, times, interlocking_time, last_action = [np.zeros_like(actions[0])] for index, time in enumerate(times): action = actions[index] - parameters = " Error during set action " + str(dead_time) + " " \ + parameters = " Error during set action " \ + str(interlocking_time) + " " + str(action) + " " + str(time) time_steps = converter.set_action(action, time) for time_step in time_steps: @@ -354,10 +311,10 @@ def continuous_converter_functions_testing(converter, times, interlocking_time, if converter_type == 'Cont-1QC': i_in = abs(i_in) conversion = converter.convert([i_in], time_step) - voltage = comparable_voltage(converter_type, action[0], i_in, tau, interlocking_time, dead_time, + voltage = comparable_voltage(converter_type, action[0], i_in, tau, interlocking_time, last_action[0]) assert abs((voltage[0] - conversion[0])) < 1E-5, "Wrong voltage: " + \ - str([tau, dead_time, interlocking_time, action, + str([tau, interlocking_time, action, last_action, time_step, i_in, conversion, voltage]) params = parameters + " " + str(i_in) + " " + str(time_step) + " " + str(conversion) @@ -367,11 +324,8 @@ def continuous_converter_functions_testing(converter, times, interlocking_time, last_action = action -def comparable_voltage(converter_type, action, i_in, tau, interlocking_time, dead_time, last_action): - if dead_time: - voltage = np.array([last_action]) - else: - voltage = np.array([action]) +def comparable_voltage(converter_type, action, i_in, tau, interlocking_time, last_action): + voltage = np.array([action]) error = np.array([- np.sign(i_in) / tau * interlocking_time]) if converter_type == 'Cont-2QC': voltage += error @@ -388,8 +342,7 @@ def comparable_voltage(converter_type, action, i_in, tau, interlocking_time, dea @pytest.mark.parametrize("tau", g_taus) @pytest.mark.parametrize("interlocking_time", g_interlocking_times) -@pytest.mark.parametrize("dead_time", g_dead_times) -def test_continuous_multi_power_electronic_converter(tau, interlocking_time, dead_time): +def test_continuous_multi_power_electronic_converter(tau, interlocking_time): """ test functions of continuous double converter :return: @@ -402,25 +355,23 @@ def test_continuous_multi_power_electronic_converter(tau, interlocking_time, dea for conv_2 in all_single_cont_converter: # setup converter with all possible combinations converter = make_module(cv.PowerElectronicConverter, 'Cont-Multi', tau=tau, - interlocking_time=interlocking_time, dead_time=dead_time, + interlocking_time=interlocking_time, subconverters=[conv_1, conv_2]) assert all(converter.reset() == np.concatenate([[-0.5, -0.5, -0.5] if ('Cont-B6C' == conv) else [0] for conv in [conv_1, conv_2]])) action_space = converter.action_space seed(123) actions = [uniform(action_space.low, action_space.high) for _ in range(0, 100)] - continuous_multi_converter_functions_testing(converter, times, interlocking_time, dead_time, actions, + continuous_multi_converter_functions_testing(converter, times, interlocking_time, actions, [conv_1, conv_2]) -def continuous_multi_converter_functions_testing(converter, times, interlocking_time, dead_time, actions, - converter_type): +def continuous_multi_converter_functions_testing(converter, times, interlocking_time, actions, converter_type): """ test function for conversion :param converter: instantiated converter :param times: times for set actions :param interlocking_time: used interlocking time - :param dead_time: used dead time :param actions: random actions :param converter_type: converter name (string) :return: @@ -429,7 +380,7 @@ def continuous_multi_converter_functions_testing(converter, times, interlocking_ last_action = np.zeros_like(actions[0]) for index, time in enumerate(times): action = actions[index] - parameters = " Error during set action " + str(dead_time) + " " \ + parameters = " Error during set action " \ + str(interlocking_time) + " " + str(action) + " " + str(time) time_steps = converter.set_action(action, time) for time_step in time_steps: @@ -449,9 +400,9 @@ def continuous_multi_converter_functions_testing(converter, times, interlocking_ "Error, does not hold limits:" + str(params) # test if the single phase converters work independent and correct for singlephase subsystems if 'Cont-B6C' not in converter_type: - voltage_0 = comparable_voltage(converter_type[0], action[0], i_in_0[0], tau, interlocking_time, dead_time, + voltage_0 = comparable_voltage(converter_type[0], action[0], i_in_0[0], tau, interlocking_time, last_action[0]) - voltage_1 = comparable_voltage(converter_type[1], action[1], i_in_1[0], tau, interlocking_time, dead_time, + voltage_1 = comparable_voltage(converter_type[1], action[1], i_in_1[0], tau, interlocking_time, last_action[1]) assert abs(voltage_0 - conversion[0]) < 1E-5, "Wrong converter value for armature circuit" assert abs(voltage_1 - conversion[1]) < 1E-5, "Wrong converter value for excitation circuit" @@ -475,10 +426,9 @@ def test_discrete_b6_bridge(): converter_default_init_1 = make_module(cv.PowerElectronicConverter, 'Finite-B6C') converter_default_init_2 = cv.FiniteB6BridgeConverter() assert converter_default_init_1._tau == 1E-5 - for subconverter in converter_default_init_1._subconverters: + for subconverter in converter_default_init_1._sub_converters: assert subconverter._tau == 1E-5 assert subconverter._interlocking_time == 0 - assert subconverter._dead_time is False # test default initialized converter converters_default = [converter_default_init_1, converter_default_init_2] @@ -505,7 +455,7 @@ def test_discrete_b6_bridge(): for time_step in time_steps: i_in[k] = [i_in_] voltage = converter.convert(i_in, time_step) - assert voltage[k] == 0.5 * u_out[step_counter], "Wrong action without dead time " + str( + assert voltage[k] == 0.5 * u_out[step_counter], "Wrong action " + str( step_counter) step_counter += 1 @@ -513,10 +463,9 @@ def test_discrete_b6_bridge(): converter_init_1 = make_module(cv.PowerElectronicConverter, 'Finite-B6C', **cf.converter_parameter) converter_init_2 = cv.FiniteB6BridgeConverter(**cf.converter_parameter) assert converter_init_1._tau == cf.converter_parameter['tau'] - for subconverter in converter_init_1._subconverters: + for subconverter in converter_init_1._sub_converters: assert subconverter._tau == cf.converter_parameter['tau'] assert subconverter._interlocking_time == cf.converter_parameter['interlocking_time'] - assert subconverter._dead_time == cf.converter_parameter['dead_time'] # set parameter actions = [6, 6, 4, 5, 1, 2, 3, 7, 0, 4] i_ins = [[[0.5], [0.5], [-0.5]], @@ -593,7 +542,6 @@ def test_continuous_b6_bridge(): for converter in converters_default: # parameter testing assert converter._tau == 1E-4 - assert converter._dead_time is False assert converter._interlocking_time == 0 assert all(converter.reset() == -0.5 * np.ones(3)) assert converter.action_space.shape == (3,) @@ -601,7 +549,6 @@ def test_continuous_b6_bridge(): assert all(converter.action_space.high == 1 * np.ones(3)) for subconverter in converter._subconverters: assert subconverter._tau == 1E-4 - assert subconverter._dead_time is False assert subconverter._interlocking_time == 0 # conversion testing for time, action, i_in in zip(times, actions, i_ins): @@ -630,7 +577,6 @@ def test_continuous_b6_bridge(): for converter in converters: # parameter testing assert converter._tau == cf.converter_parameter['tau'] - assert converter._dead_time == cf.converter_parameter['dead_time'] assert converter._interlocking_time == cf.converter_parameter['interlocking_time'] assert all(converter.reset() == -0.5 * np.ones(3)) assert converter.action_space.shape == (3,) @@ -638,7 +584,6 @@ def test_continuous_b6_bridge(): assert all(converter.action_space.high == 1 * np.ones(3)) for subconverter in converter._subconverters: assert subconverter._tau == cf.converter_parameter['tau'] - assert subconverter._dead_time == cf.converter_parameter['dead_time'] assert subconverter._interlocking_time == cf.converter_parameter['interlocking_time'] # conversion testing for time, action, i_in, expected_voltage in zip(times, actions, i_ins, expected_voltages): @@ -664,16 +609,15 @@ class TestPowerElectronicConverter: @pytest.fixture def converter(self): - return self.class_to_test(tau=0, dead_time=False, interlocking_time=0) + return self.class_to_test(tau=0, interlocking_time=0) - @pytest.mark.parametrize("tau, dead_time, interlocking_time, kwargs", [ - (1, True, 0.1, {}), - (0.1, False, 0.0, {}), + @pytest.mark.parametrize("tau, interlocking_time", [ + (1, 0.1), + (0.1, 0.0), ]) - def test_initialization(self, tau, dead_time, interlocking_time, kwargs): - converter = self.class_to_test(tau=tau, dead_time=dead_time, interlocking_time=interlocking_time, **kwargs) + def test_initialization(self, tau, interlocking_time): + converter = self.class_to_test(tau=tau, interlocking_time=interlocking_time) assert converter._tau == tau - assert converter._dead_time == dead_time assert converter._interlocking_time == interlocking_time def test_registered(self): @@ -682,28 +626,23 @@ def test_registered(self): assert type(conv) == self.class_to_test def test_reset(self, converter): - assert converter._dead_time_action == converter._reset_action == converter._current_action assert converter._action_start_time == 0 assert np.all(converter.reset() == np.array([0])) @pytest.mark.parametrize("action_space", [Discrete(3), Box(-1, 1, shape=(1,))]) def test_set_action(self, monkeypatch, converter, action_space): monkeypatch.setattr(converter, "_set_switching_pattern", lambda: [0.0]) - monkeypatch.setattr(converter, "_dead_time", False) monkeypatch.setattr(converter, "action_space", converter.action_space or action_space) next_action = converter.action_space.sample() converter.set_action(next_action, 0) assert np.all(converter._current_action == next_action) assert converter._action_start_time == 0 - monkeypatch.setattr(converter, "_dead_time", True) action_2 = converter.action_space.sample() action_3 = converter.action_space.sample() while np.all(action_3 == action_2): action_3 = converter.action_space.sample() converter.set_action(action_2, 1) converter.set_action(action_3, 2) - assert np.all(converter._current_action == action_2) - assert np.all(converter._dead_time_action == action_3) assert converter._action_start_time == 2 @@ -1129,7 +1068,7 @@ def test_registered(self): dummy_converters[2].action_space = Box(0, 1, shape=(1,)) conv = gem.utils.instantiate(cv.PowerElectronicConverter, self.key, subconverters=dummy_converters) assert type(conv) == self.class_to_test - assert conv._subconverters == dummy_converters + assert conv._sub_converters == dummy_converters @pytest.mark.parametrize("tau, dead_time, interlocking_time, kwargs", [ (1, True, 0.1, {'subconverters': ['Cont-1QC', 'Cont-B6C', 'Cont-4QC']}), @@ -1139,20 +1078,20 @@ def test_initialization(self, tau, dead_time, interlocking_time, kwargs): super().test_initialization(tau, dead_time, interlocking_time, kwargs) conv = self.class_to_test(tau=tau, dead_time=dead_time, interlocking_time=interlocking_time, **kwargs) assert np.all( - conv.action_space.low == np.concatenate([subconv.action_space.low for subconv in conv._subconverters]) + conv.action_space.low == np.concatenate([subconv.action_space.low for subconv in conv._sub_converters]) ), "Action space lower boundaries in the multi converter do not fit the subconverters." assert np.all( - conv.action_space.high == np.concatenate([subconv.action_space.high for subconv in conv._subconverters]) + conv.action_space.high == np.concatenate([subconv.action_space.high for subconv in conv._sub_converters]) ), "Action space upper boundaries in the multi converter do not fit the subconverters." assert np.all( conv.subsignal_voltage_space_dims == - np.array([(np.squeeze(subconv.voltages.shape) or 1) for subconv in conv._subconverters]) + np.array([(np.squeeze(subconv.voltages.shape) or 1) for subconv in conv._sub_converters]) ), "Voltage space dims in the multi converter do not fit the subconverters." assert np.all( conv.subsignal_current_space_dims == - np.array([(np.squeeze(subconv.currents.shape) or 1) for subconv in conv._subconverters]) + np.array([(np.squeeze(subconv.currents.shape) or 1) for subconv in conv._sub_converters]) ), "Current space dims in the multi converter do not fit the subconverters." - for sc in conv._subconverters: + for sc in conv._sub_converters: assert sc._interlocking_time == interlocking_time assert sc._dead_time == dead_time assert sc._tau == tau @@ -1160,7 +1099,7 @@ def test_initialization(self, tau, dead_time, interlocking_time, kwargs): def test_reset(self, converter): u_in = converter.reset() assert u_in == [0.0] * converter.voltages.shape[0] - assert np.all([subconv.reset_counter == 1 for subconv in converter._subconverters]) + assert np.all([subconv.reset_counter == 1 for subconv in converter._sub_converters]) def test_action_clipping(self, converter): # Done by the subconverters @@ -1170,9 +1109,9 @@ def test_action_clipping(self, converter): def test_set_action(self, monkeypatch, converter, action): t = np.random.randint(10) * converter._tau converter.set_action(action, t) - sc0 = converter._subconverters[0] - sc1 = converter._subconverters[1] - sc2 = converter._subconverters[2] + sc0 = converter._sub_converters[0] + sc1 = converter._sub_converters[1] + sc2 = converter._sub_converters[2] assert np.all(np.concatenate((sc0.action, sc1.action, sc2.action)) == action) assert sc0.action_set_time == t assert sc1.action_set_time == t @@ -1186,7 +1125,7 @@ def test_convert(self, monkeypatch, converter, i_out): u = converter.convert(i_out, t) sub_u = [] start_idx = 0 - for subconverter, subaction_space_size in zip(converter._subconverters, action_space_size): + for subconverter, subaction_space_size in zip(converter._sub_converters, action_space_size): end_idx = start_idx + subaction_space_size assert subconverter.i_out == i_out[start_idx:end_idx] start_idx = end_idx diff --git a/tests/test_physical_systems/test_noise_generator.py b/tests/test_physical_systems/test_noise_generator.py deleted file mode 100644 index 08af66d2..00000000 --- a/tests/test_physical_systems/test_noise_generator.py +++ /dev/null @@ -1,258 +0,0 @@ -from gym_electric_motor.physical_systems.noise_generators import * -import gym_electric_motor.physical_systems.noise_generators as ng -import numpy as np -import pytest - - -class TestNoiseGenerator: - """Class to test the basic functions of each NoiseGenerator""" - - # defined test values - _state_variables = ['omega', 'torque', 'u', 'i'] - _test_state_name = ['omega', 'torque', 'u_a', 'u_e', 'i_a', 'i_e'] - _test_signal_powers = [0.7, 0.6, 0.5, 0.4] - _state_length = 4 # Number of elements in _state_variables - _test_noise = np.ones(_state_length) * 0.25 - _test_object = NoiseGenerator() - - # counter - _monkey_noise_counter = 0 - _monkey_set_state_names = 0 - _monkey_set_state_array_none = 0 - - def monkey_noise(self, *_, **__): - """ - mock function for noise() - :param _: - :param __: - :return: - """ - self._monkey_noise_counter += 1 - return self._test_noise - - def monkey_set_state_array(self, values, names): - """ - mock function for utils.set_state_array() - :param values: - :param names: - :return: - """ - assert len(values) == len(names), 'different number of values and names' - assert values == self._test_signal_powers, 'values are not the test signal powers' - assert names == self._state_variables, 'names are not the state variables' - return np.array(values) - - def monkey_set_state_array_none(self, values, names): - """ - mock function for utils.set_state_array() - This function is for the case if names should be none - :param values: - :param names: - :return: - """ - self._monkey_set_state_array_none += 1 - assert names is None - return np.array(values) - - def monkey_set_state_names(self, state_names): - """ - mock function for set_state_names - :param state_names: - :return: - """ - self._monkey_set_state_names += 1 - assert state_names == self._state_variables, 'names are not the state variables' - - def test_noise(self, monkeypatch): - """ - test noise() - :param monkeypatch: - :return: - """ - # setup test scenario - self._test_object = NoiseGenerator() - monkeypatch.setattr(NoiseGenerator, "_state_variables", self._state_variables) - # call function to test - test_noise = self._test_object.noise() - # verify the expected results - assert all(test_noise == np.zeros(self._state_length)), 'noise is not zero' - - def test_reset(self, monkeypatch): - """ - test reset() - :param monkeypatch: - :return: - """ - # setup test scenario - self._test_object = NoiseGenerator() - self._test_noise = np.ones(self._state_length) * 0.25 - monkeypatch.setattr(NoiseGenerator, "noise", self.monkey_noise) - # call function to test - test_value = self._test_object.reset() - # verify the expected results - assert all(test_value == self._test_noise), 'noise is not as expected' - assert self._monkey_noise_counter == 1, '_noise() not called once' - - def test_set_state_names(self): - """ - test set_state_names() - :return: - """ - # call function to test - - self._test_object.set_state_names(self._test_state_name) - # verify the expected results - assert self._test_object._state_variables == self._test_state_name, 'names are not the state variables' - - def test_set_signal_power(self, monkeypatch): - """ - test set_signal_power() - :param monkeypatch: - :return: - """ - # setup test scenario - self._test_object = NoiseGenerator() - monkeypatch.setattr(NoiseGenerator, "_state_variables", self._state_variables) - monkeypatch.setattr(ng, "set_state_array", self.monkey_set_state_array) - # call function to test - self._test_object.set_signal_power_level(self._test_signal_powers) - # verify the expected results - assert all(self._test_object._signal_powers == np.array(self._test_signal_powers)), \ - 'signal powers are not set correctly' - - # test for warning - # setup test scenario - monkeypatch.setattr(NoiseGenerator, "_state_variables", None) - monkeypatch.setattr(ng, "set_state_array", self.monkey_set_state_array_none) - with pytest.warns(UserWarning): - # call function to test - self._test_object.set_signal_power_level(self._test_signal_powers) - # verify the expected results - assert all(self._test_object._signal_powers == np.array(self._test_signal_powers)), \ - 'signal powers are not set correctly' - - -class TestGaussianWhiteNoiseGenerator(TestNoiseGenerator): - """Class to test the GaussianWhiteNoiseGenerator""" - - # defined test values - _default_noise_levels = 0.0 - _default_noise_length = 10000 - _noise_length = 10 - _noise_levels = None - _state_length = 4 - _test_noise = np.ones((_noise_length, _state_length)) * 0.42 - - # counter - _monkey_set_state_array_noise_levels_counter = 0 - _monkey_noise_generator_counter = 0 - - def monkey_set_state_array_noise_levels(self, values, names): - """ - mock function for set_state_array() - :param values: - :param names: - :return: - """ - self._monkey_set_state_array_noise_levels_counter += 1 - assert values == self._noise_levels, 'values are not the noise levels' - assert names == self._state_variables, 'names are not the state variables' - if type(values) is list: - assert len(values) == len(names), 'different number of values and names' - result = np.array(values) - elif type(values) is dict: - assert len(values) == len(names), 'different number of values and names' - result = np.array(list(values.values())) - elif type(values) is np.ndarray: - assert len(values) == len(names), 'different number of values and names' - result = values - elif type(values) is float: - result = values * np.ones(len(names)) - else: - raise Exception("Not existing type") - return result - - def monkey_noise_generator(self): - """ - mock function for noise generator() - :return: - """ - self._monkey_noise_generator_counter += 1 - self._test_object._noise = np.ones((self._noise_length, self._state_length)) - self._test_object._noise_pointer = 0 - - def test_init_default(self): - """ - test initialization without parameters - :return: - """ - # call function to test - self._test_object = GaussianWhiteNoiseGenerator() - # verify the expected results - assert self._test_object._noise_levels == self._default_noise_levels,\ - 'initialized noise levels are not the default ones' - assert self._test_object._noise_length == self._test_object._noise_pointer == self._default_noise_length, \ - 'initialized noise length is not the default one' - - @pytest.mark.parametrize("noise_levels", [0.1, [0.1, 0.3, 0.2, 0.5], - dict(omega=0.1, torque=0.4, u=0.2, i=0.6)]) - @pytest.mark.parametrize("noise_length", [_noise_length]) - def test_init_parametrized(self, noise_levels, noise_length): - """ - test parametrized initialization - :param noise_levels: possible noise levels - :param noise_length: possible noise length - :return: - """ - # call function to test - self._test_object = GaussianWhiteNoiseGenerator(noise_levels, noise_length) - # verify the expected results - assert noise_levels == self._test_object._noise_levels, 'noise levels are not passed correctly' - assert self._test_object._noise_length == self._test_object._noise_pointer == noise_length,\ - 'noise length is not passed correctly' - - @pytest.mark.parametrize("noise_levels", [0.1, [0.1, 0.3, 0.2, 0.5], - dict(omega=0.1, torque=0.4, u=0.2, i=0.6)]) - def test_set_state_names(self, monkeypatch, noise_levels): - """ - test set state names - :param monkeypatch: - :param noise_levels: possible noise levels - :return: - """ - # setup test scenario - monkeypatch.setattr(NoiseGenerator, "set_state_names", self.monkey_set_state_names) - self._test_object = GaussianWhiteNoiseGenerator() - monkeypatch.setattr(self._test_object, "_noise_levels", noise_levels) - monkeypatch.setattr(ng, "set_state_array", self.monkey_set_state_array_noise_levels) - self._noise_levels = noise_levels - # call function to test - self._test_object.set_state_names(self._state_variables) - # verify the expected results - assert type(self._test_object._noise_levels) is np.ndarray - assert self._monkey_set_state_array_noise_levels_counter == 1, 'set_state_array() not called once' - assert self._monkey_set_state_names == 1, 'set_state_names not called once' - - @pytest.mark.parametrize("noise_pointer, result_noise_pointer, count_generate_noise", - [(0, 1, 0), (1, 2, 0), (3, 4, 0), (8, 9, 0), (9, 10, 0), (10, 1, 1)]) - def test_noise(self, monkeypatch, noise_pointer, result_noise_pointer, count_generate_noise): - """ - test noise() - :param monkeypatch: - :param noise_pointer: current step in the noise array - :param result_noise_pointer: next step in the noise array - :param count_generate_noise: counter for generate_noise() - :return: - """ - # setup test scenario - self._test_object = GaussianWhiteNoiseGenerator() - monkeypatch.setattr(self._test_object, "_noise_length", self._noise_length) - monkeypatch.setattr(self._test_object, "_noise_pointer", noise_pointer) - monkeypatch.setattr(self._test_object, "_noise", self._test_noise) - monkeypatch.setattr(GaussianWhiteNoiseGenerator, "_generate_noise", self.monkey_noise_generator) - # call function to test - noise = self._test_object.noise() - # verify the expected results - assert len(noise) == self._state_length, 'Wrong noise length' - assert self._test_object._noise_pointer == result_noise_pointer, ' unexpected noise pointer' - assert self._monkey_noise_generator_counter == count_generate_noise, 'noise_generator() called unexpected often' diff --git a/tests/test_physical_systems/test_physical_systems.py b/tests/test_physical_systems/test_physical_systems.py index 3134294d..264ea64d 100644 --- a/tests/test_physical_systems/test_physical_systems.py +++ b/tests/test_physical_systems/test_physical_systems.py @@ -1,5 +1,5 @@ import numpy as np -from ..testing_utils import DummyConverter, DummyLoad, DummyNoise, DummyOdeSolver, DummyVoltageSupply, DummyElectricMotor,\ +from ..testing_utils import DummyConverter, DummyLoad, DummyOdeSolver, DummyVoltageSupply, DummyElectricMotor,\ mock_instantiate, instantiate_dict from gym_electric_motor.physical_systems import physical_systems as ps, converters as cv, electric_motors as em,\ mechanical_loads as ml, voltage_supplies as vs, solvers as sv @@ -50,7 +50,6 @@ def scml_system(self, monkeypatch): load=DummyLoad(), supply=DummyVoltageSupply(), ode_solver=DummyOdeSolver(), - noise_generator=DummyNoise() ) def test_reset(self, scml_system): @@ -60,7 +59,7 @@ def test_reset(self, scml_system): state_space = scml_system.state_space state_positions = scml_system.state_positions initial_state = scml_system.reset() - target = (np.array([0, 0, 0, 0, 0, 0, 560]) + scml_system._noise_generator.reset()) / scml_system.limits + target = np.array([0, 0, 0, 0, 0, 0, 560]) / scml_system.limits assert np.all(initial_state == target), 'Initial states of the system are incorrect' assert scml_system._t == 0, 'Time of the system was not set to zero after reset' assert scml_system._k == 0, 'Episode step of the system was not set to zero after reset' @@ -106,7 +105,6 @@ def test_simulate(self, scml_system): # Calculate the next state desired_next_state = ( np.concatenate((solver_state_me, torque, solver_state_el, u_in, u_sup)) - + scml_system._noise_generator.noise() ) / scml_system.limits # Assertions for correct simulation diff --git a/tests/testing_utils.py b/tests/testing_utils.py index 7280a267..6c669213 100644 --- a/tests/testing_utils.py +++ b/tests/testing_utils.py @@ -4,7 +4,7 @@ from gym_electric_motor import ReferenceGenerator, RewardFunction, PhysicalSystem, ElectricMotorVisualization, \ ConstraintMonitor from gym_electric_motor.physical_systems import PowerElectronicConverter, MechanicalLoad, ElectricMotor, OdeSolver, \ - VoltageSupply, NoiseGenerator + VoltageSupply import gym_electric_motor.physical_systems.converters as cv from gym_electric_motor.physical_systems.physical_systems import SCMLSystem import numpy as np @@ -543,23 +543,6 @@ def get_state_space(self, omega_range): return {'omega': 0, 'position': -1}, {'omega': 1, 'position': -1} -class DummyNoise(NoiseGenerator): - """ - dummy class for noise generator - """ - - def __init__(self, **kwargs): - self.kwargs = kwargs - self.reset_counter = 0 - super().__init__() - - def reset(self): - return np.ones_like(self._state_variables, dtype=float) * 0.36 - - def noise(self, *_, **__): - return np.ones_like(self._state_variables, dtype=float) * 0.42 - - class DummyOdeSolver(OdeSolver): """ Dummy class for ode solver From b90ef4c7fc42e440eefd8f80506f64a676153a62 Mon Sep 17 00:00:00 2001 From: Arne Date: Sun, 16 Jan 2022 19:49:50 +0100 Subject: [PATCH 20/79] Added the possibility to remove the angle to the CosSinProcessor --- .../cos_sin_processor.py | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/gym_electric_motor/state_action_processors/cos_sin_processor.py b/gym_electric_motor/state_action_processors/cos_sin_processor.py index 01e27111..c04f4ea1 100644 --- a/gym_electric_motor/state_action_processors/cos_sin_processor.py +++ b/gym_electric_motor/state_action_processors/cos_sin_processor.py @@ -7,6 +7,8 @@ class CosSinProcessor(StateActionProcessor): """Adds ``cos(angle)`` and ``sin(angle)`` states to the systems state vector that are the cosine and sine of a certain systems state. + + Optionally, the CosSinProcessor can also remove the angle from the state vector. """ @property @@ -14,27 +16,34 @@ def angle(self): """Returns the name of the state whose cosine and sine are appended to the state vector.""" return self._angle - def __init__(self, angle='epsilon', physical_system=None): + def __init__(self, angle='epsilon', physical_system=None, remove_angle=False): """ Args: angle(string): Name of the state whose cosine and sine will be added to the systems state vector. Default: ``'epsilon'`` physical_system(PhysicalSystem(optional)): Inner system of this processor. + remove_angle(bool): Remove the angle from the state vector """ self._angle = angle self._angle_index = None + self._remove_angle = remove_angle + self._remove_idx = [] super().__init__(physical_system) def set_physical_system(self, physical_system): # Docstring of super class super().set_physical_system(physical_system) - low = np.concatenate((physical_system.state_space.low, [-1., -1.])) - high = np.concatenate((physical_system.state_space.high, [1., 1.])) - self.state_space = gym.spaces.Box(low, high, dtype=np.float64) self._angle_index = physical_system.state_positions[self._angle] - self._limits = np.concatenate((physical_system.limits, [1., 1.])) - self._nominal_state = np.concatenate((physical_system.nominal_state, [1., 1.])) - self._state_names = physical_system.state_names + [f'cos({self._angle})', f'sin({self._angle})'] + + self._remove_idx = [self._angle_index] if self._remove_angle else [] + low = np.concatenate((np.delete(physical_system.state_space.low, self._remove_idx), [-1., -1.])) + high = np.concatenate((np.delete(physical_system.state_space.high, self._remove_idx), [1., 1.])) + self.state_space = gym.spaces.Box(low, high, dtype=np.float64) + + self._limits = np.concatenate((np.delete(physical_system.limits, self._remove_idx), [1., 1.])) + self._nominal_state = np.concatenate((np.delete(physical_system.nominal_state, self._remove_idx), [1., 1.])) + self._state_names = list(np.delete(physical_system.state_names, self._remove_idx)) \ + + [f'cos({self._angle})', f'sin({self._angle})'] self._state_positions = {key: index for index, key in enumerate(self._state_names)} return self @@ -46,8 +55,21 @@ def reset(self): def simulate(self, action): # Docstring of super class state = self._physical_system.simulate(action) + if self._remove_angle: + return self._delete_angle(self._get_cos_sin(state)) return self._get_cos_sin(state) + def _delete_angle(self, state): + """Removes the angle from the state vector + + Args: + state(numpy.ndarray[float]): The state vector of the system. + + Returns: + numpy.ndarray[float]: The state vector removed by the angle. + """ + return np.delete(state, self._remove_idx) + def _get_cos_sin(self, state): """Appends the cosine and sine of the specified state to the state vector. From 10f89640f49de1008e6855a5bcf251b8fe58c642 Mon Sep 17 00:00:00 2001 From: Arne Date: Thu, 20 Jan 2022 22:02:45 +0100 Subject: [PATCH 21/79] Test fixes --- .../physical_systems/converters.py | 39 ++++---- .../cos_sin_processor.py | 2 +- tests/conf.py | 2 +- .../test_physical_systems/test_converters.py | 97 +++++++++---------- tests/testing_utils.py | 4 +- 5 files changed, 70 insertions(+), 74 deletions(-) diff --git a/gym_electric_motor/physical_systems/converters.py b/gym_electric_motor/physical_systems/converters.py index 317948ea..bd1feb77 100644 --- a/gym_electric_motor/physical_systems/converters.py +++ b/gym_electric_motor/physical_systems/converters.py @@ -115,6 +115,7 @@ def i_sup(self, i_out): def convert(self, i_out, t): return [1] + class ContDynamicallyAveragedConverter(PowerElectronicConverter): """ Base class for all continuously controlled converters that calculate the input voltages to the motor with a @@ -485,8 +486,8 @@ class FiniteMultiConverter(FiniteConverter): """ @property - def subconverters(self): - return self._subconverters + def sub_converters(self): + return self._sub_converters def __init__(self, subconverters, **kwargs): """ @@ -495,7 +496,7 @@ def __init__(self, subconverters, **kwargs): kwargs(dict): Parameters to pass to the Subconverters and the superclass """ super().__init__(**kwargs) - self._subconverters = [ + self._sub_converters = [ instantiate(PowerElectronicConverter, subconverter, **kwargs) for subconverter in subconverters ] self.subsignal_current_space_dims = [] @@ -507,7 +508,7 @@ def __init__(self, subconverters, **kwargs): voltages_high = [] # get the limits and space dims from each subconverter - for subconverter in self._subconverters: + for subconverter in self._sub_converters: self.subsignal_current_space_dims.append(np.squeeze(subconverter.currents.shape) or 1) self.subsignal_voltage_space_dims.append(np.squeeze(subconverter.voltages.shape) or 1) @@ -538,7 +539,7 @@ def convert(self, i_out, t): # Docstring in base class u_in = [] subsignal_idx_low = 0 - for subconverter, subsignal_space_size in zip(self._subconverters, self.subsignal_voltage_space_dims): + for subconverter, subsignal_space_size in zip(self._sub_converters, self.subsignal_voltage_space_dims): subsignal_idx_high = subsignal_idx_low + subsignal_space_size u_in += subconverter.convert(i_out[subsignal_idx_low:subsignal_idx_high], t) subsignal_idx_low = subsignal_idx_high @@ -547,14 +548,14 @@ def convert(self, i_out, t): def reset(self): # Docstring in base class u_in = [] - for subconverter in self._subconverters: + for subconverter in self._sub_converters: u_in += subconverter.reset() return u_in def set_action(self, action, t): # Docstring in base class times = [] - for subconverter, sub_action in zip(self._subconverters, action): + for subconverter, sub_action in zip(self._sub_converters, action): times += subconverter.set_action(sub_action, t) return sorted(list(set(times))) @@ -562,7 +563,7 @@ def i_sup(self, i_out): # Docstring in base class i_sup = 0 subsignal_idx_low = 0 - for subconverter, subsignal_space_size in zip(self._subconverters, self.subsignal_current_space_dims): + for subconverter, subsignal_space_size in zip(self._sub_converters, self.subsignal_current_space_dims): subsignal_idx_high = subsignal_idx_low + subsignal_space_size i_sup += subconverter.i_sup(i_out[subsignal_idx_low:subsignal_idx_high]) subsignal_idx_low = subsignal_idx_high @@ -743,7 +744,7 @@ class FiniteB6BridgeConverter(FiniteConverter): def __init__(self, tau=1e-5, **kwargs): # Docstring in base class super().__init__(tau=tau, **kwargs) - self._subconverters = [ + self._sub_converters = [ FiniteTwoQuadrantConverter(tau=tau, **kwargs), FiniteTwoQuadrantConverter(tau=tau, **kwargs), FiniteTwoQuadrantConverter(tau=tau, **kwargs), @@ -752,17 +753,17 @@ def __init__(self, tau=1e-5, **kwargs): def reset(self): # Docstring in base class return [ - self._subconverters[0].reset()[0] - 0.5, - self._subconverters[1].reset()[0] - 0.5, - self._subconverters[2].reset()[0] - 0.5, + self._sub_converters[0].reset()[0] - 0.5, + self._sub_converters[1].reset()[0] - 0.5, + self._sub_converters[2].reset()[0] - 0.5, ] def convert(self, i_out, t): # Docstring in base class u_out = [ - self._subconverters[0].convert([i_out[0]], t)[0] - 0.5, - self._subconverters[1].convert([i_out[1]], t)[0] - 0.5, - self._subconverters[2].convert([i_out[2]], t)[0] - 0.5 + self._sub_converters[0].convert([i_out[0]], t)[0] - 0.5, + self._sub_converters[1].convert([i_out[1]], t)[0] - 0.5, + self._sub_converters[2].convert([i_out[2]], t)[0] - 0.5 ] return u_out @@ -772,14 +773,14 @@ def set_action(self, action, t): f"The selected action {action} is not a valid element of the action space {self.action_space}." subactions = self._subactions[action] times = [] - times += self._subconverters[0].set_action(subactions[0], t) - times += self._subconverters[1].set_action(subactions[1], t) - times += self._subconverters[2].set_action(subactions[2], t) + times += self._sub_converters[0].set_action(subactions[0], t) + times += self._sub_converters[1].set_action(subactions[1], t) + times += self._sub_converters[2].set_action(subactions[2], t) return sorted(list(set(times))) def i_sup(self, i_out): # Docstring in base class - return sum([subconverter.i_sup([i_out_]) for subconverter, i_out_ in zip(self._subconverters, i_out)]) + return sum([subconverter.i_sup([i_out_]) for subconverter, i_out_ in zip(self._sub_converters, i_out)]) class ContB6BridgeConverter(ContDynamicallyAveragedConverter): diff --git a/gym_electric_motor/state_action_processors/cos_sin_processor.py b/gym_electric_motor/state_action_processors/cos_sin_processor.py index c04f4ea1..b3ce2fc0 100644 --- a/gym_electric_motor/state_action_processors/cos_sin_processor.py +++ b/gym_electric_motor/state_action_processors/cos_sin_processor.py @@ -35,7 +35,7 @@ def set_physical_system(self, physical_system): super().set_physical_system(physical_system) self._angle_index = physical_system.state_positions[self._angle] - self._remove_idx = [self._angle_index] if self._remove_angle else [] + self._remove_idx = self._angle_index if self._remove_angle else [] low = np.concatenate((np.delete(physical_system.state_space.low, self._remove_idx), [-1., -1.])) high = np.concatenate((np.delete(physical_system.state_space.high, self._remove_idx), [1., 1.])) self.state_space = gym.spaces.Box(low, high, dtype=np.float64) diff --git a/tests/conf.py b/tests/conf.py index 2a4862af..830ee6c3 100644 --- a/tests/conf.py +++ b/tests/conf.py @@ -122,7 +122,7 @@ load_parameter = {'j_load': 0.2, 'state_names': ['omega'], 'j_rot_load': 0.25, 'omega_range': (0, 1), 'parameter': dict(a=0.12, b=0.13, c=0.4, j_load=0.2)} -converter_parameter = {'tau': 2E-4, 'dead_time': True, 'interlocking_time': 1E-6} +converter_parameter = {'tau': 2E-4, 'interlocking_time': 1E-6} test_motor_parameter = {'DcSeries': series_motor_parameter, 'DcShunt': shunt_motor_parameter, diff --git a/tests/test_physical_systems/test_converters.py b/tests/test_physical_systems/test_converters.py index d7eb579e..ec6cecc3 100644 --- a/tests/test_physical_systems/test_converters.py +++ b/tests/test_physical_systems/test_converters.py @@ -434,7 +434,7 @@ def test_discrete_b6_bridge(): converters_default = [converter_default_init_1, converter_default_init_2] for converter in converters_default: assert all(converter.reset() == -0.5 * np.ones(3)) - assert converter._subconverters[0].action_space.n == 3 + assert converter._sub_converters[0].action_space.n == 3 assert converter.action_space.n == 8 # 1 1 1 1 2 2 2 1 2 1 # Action for the converter @@ -455,8 +455,7 @@ def test_discrete_b6_bridge(): for time_step in time_steps: i_in[k] = [i_in_] voltage = converter.convert(i_in, time_step) - assert voltage[k] == 0.5 * u_out[step_counter], "Wrong action " + str( - step_counter) + assert voltage[k] == 0.5 * u_out[step_counter], "Wrong action " + str(step_counter) step_counter += 1 # test parametrized converter @@ -479,8 +478,7 @@ def test_discrete_b6_bridge(): [[0.5], [-0.5], [0]], [[0.5], [-0.5], [0.5]]] - expected_voltage = np.array([[-1, -1, 1], - [1, 1, -1], + expected_voltage = np.array([[1, 1, -1], [1, 1, -1], [1, -1, -1], [1, -1, -1], @@ -495,7 +493,8 @@ def test_discrete_b6_bridge(): [-1, 1, 1], [1, 1, 1], [-1, 1, -1], - [-1, -1, -1]]) / 2 + [-1, -1, -1], + [1, -1, -1]]) / 2 times = np.arange(len(actions)) * tau converters_init = [converter_init_1, converter_init_2] @@ -631,7 +630,7 @@ def test_reset(self, converter): @pytest.mark.parametrize("action_space", [Discrete(3), Box(-1, 1, shape=(1,))]) def test_set_action(self, monkeypatch, converter, action_space): - monkeypatch.setattr(converter, "_set_switching_pattern", lambda: [0.0]) + monkeypatch.setattr(converter, "_set_switching_pattern", lambda action: [0.0]) monkeypatch.setattr(converter, "action_space", converter.action_space or action_space) next_action = converter.action_space.sample() converter.set_action(next_action, 0) @@ -652,7 +651,7 @@ class TestContDynamicallyAveragedConverter(TestPowerElectronicConverter): @pytest.fixture def converter(self, monkeypatch): - converter = self.class_to_test(tau=0.1, dead_time=False, interlocking_time=0) + converter = self.class_to_test(tau=0.1, interlocking_time=0) converter.action_space = converter.action_space or Box(-1, 1, shape=(1,)) if converter.voltages and converter.currents: converter.voltages = Box(converter.voltages.low[0], converter.voltages.high[0], shape=(1,)) #(converter.voltages.low[0] or -1, converter.voltages.high[0] or -1) @@ -748,12 +747,11 @@ class TestFiniteTwoQuadrantConverter(TestFiniteConverter): @pytest.mark.parametrize("interlocking_time", [0.0, 0.1]) def test_set_switching_pattern(self, monkeypatch, converter, interlocking_time): - monkeypatch.setattr(converter, "_dead_time", False) monkeypatch.setattr(converter, "_interlocking_time", interlocking_time) # Test if no interlocking step required after switching state 0 ( monkeypatch.setattr(converter, "_switching_state", 0) - assert converter._set_switching_pattern() == [converter._tau] + assert converter._set_switching_pattern(0) == [converter._tau] assert converter._switching_pattern == [converter._current_action] # Test if no interlocking step required, if the same action is set again monkeypatch.setattr(converter, "_switching_state", 1) @@ -975,26 +973,25 @@ def converter(self): DummyConverter(action_space=Discrete(4), voltages=Box(-1, 1, shape=(1,)), currents=Box(-1, 1, shape=(1,))), ]) - @pytest.mark.parametrize("tau, dead_time, interlocking_time, kwargs", [ - (1, True, 0.1, {'subconverters': ['Finite-1QC', 'Finite-B6C', 'Finite-4QC']}), - (0.1, False, 0.0, {'subconverters': ['Finite-1QC', 'Finite-B6C', 'Finite-4QC']}), + @pytest.mark.parametrize("tau, interlocking_time, kwargs", [ + (1, 0.1, {'subconverters': ['Finite-1QC', 'Finite-B6C', 'Finite-4QC']}), + (0.1, 0.0, {'subconverters': ['Finite-1QC', 'Finite-B6C', 'Finite-4QC']}), ]) - def test_initialization(self, tau, dead_time, interlocking_time, kwargs): - super().test_initialization(tau, dead_time, interlocking_time, kwargs) - conv = self.class_to_test(tau=tau, dead_time=dead_time, interlocking_time=interlocking_time, **kwargs) + def test_initialization(self, tau, interlocking_time, kwargs): + super().test_initialization(tau, interlocking_time, kwargs) + conv = self.class_to_test(tau=tau, interlocking_time=interlocking_time, **kwargs) assert np.all( conv.subsignal_voltage_space_dims == - np.array([(np.squeeze(subconv.voltages.shape) or 1) for subconv in conv._subconverters]) + np.array([(np.squeeze(subconv.voltages.shape) or 1) for subconv in conv._sub_converters]) ), "Voltage space dims in the multi converter do not fit the subconverters." assert np.all( conv.subsignal_current_space_dims == - np.array([(np.squeeze(subconv.currents.shape) or 1) for subconv in conv._subconverters]) + np.array([(np.squeeze(subconv.currents.shape) or 1) for subconv in conv._sub_converters]) ), "Current space dims in the multi converter do not fit the subconverters." - assert np.all(conv.action_space.nvec == [subconv.action_space.n for subconv in conv._subconverters] + assert np.all(conv.action_space.nvec == [subconv.action_space.n for subconv in conv._sub_converters] ), "Action space of the multi converter does not fit the subconverters." - for sc in conv._subconverters: + for sc in conv._sub_converters: assert sc._interlocking_time == interlocking_time - assert sc._dead_time == dead_time assert sc._tau == tau def test_registered(self): @@ -1005,13 +1002,13 @@ def test_registered(self): def test_reset(self, converter): u_in = converter.reset() assert u_in == [0.0] * converter.voltages.shape[0] - assert np.all([subconv.reset_counter == 1 for subconv in converter._subconverters]) + assert np.all([subconv.reset_counter == 1 for subconv in converter._sub_converters]) def test_set_action(self, monkeypatch, converter, **_): for action in np.ndindex(tuple(converter.action_space.nvec)): - sc0 = converter._subconverters[0] - sc1 = converter._subconverters[1] - sc2 = converter._subconverters[2] + sc0 = converter._sub_converters[0] + sc1 = converter._sub_converters[1] + sc2 = converter._sub_converters[2] t = (action[0] * sc1.action_space.n * sc2.action_space.n + action[1] * sc2.action_space.n + action[2])* converter._tau converter.set_action(action, t) @@ -1027,16 +1024,16 @@ def test_default_init(self): @pytest.mark.parametrize('i_out', [[0, 6, 2, 7, 9], [1, 0.5, 2], [-1, 1]]) def test_convert(self, converter, i_out): # Setting of the demanded output voltages from the dummy converters - for subconverter in converter._subconverters: + for subconverter in converter._sub_converters: subconverter.action = subconverter.action_space.sample() u = converter.convert(i_out, 0) assert np.all( - u == [subconv.action for subconv in converter._subconverters] + u == [subconv.action for subconv in converter._sub_converters] ) @pytest.mark.parametrize('i_out', [[0, 6, 2, 7, 9], [1, 0.5, 2, -7, 50], [-1, 1, 0.01, 16, -42]]) def test_i_sup(self, converter, i_out): - sc0, sc1, sc2 = converter._subconverters + sc0, sc1, sc2 = converter._sub_converters converter.set_action(converter.action_space.sample(), np.random.rand()) i_sup = converter.i_sup(i_out) assert sc0.last_i_out + sc1.last_i_out + sc2.last_i_out == i_out @@ -1070,13 +1067,13 @@ def test_registered(self): assert type(conv) == self.class_to_test assert conv._sub_converters == dummy_converters - @pytest.mark.parametrize("tau, dead_time, interlocking_time, kwargs", [ - (1, True, 0.1, {'subconverters': ['Cont-1QC', 'Cont-B6C', 'Cont-4QC']}), - (0.1, False, 0.0, {'subconverters': ['Cont-1QC', 'Cont-B6C', 'Cont-4QC']}), + @pytest.mark.parametrize("tau, interlocking_time, kwargs", [ + (1, 0.1, {'subconverters': ['Cont-1QC', 'Cont-B6C', 'Cont-4QC']}), + (0.1, 0.0, {'subconverters': ['Cont-1QC', 'Cont-B6C', 'Cont-4QC']}), ]) - def test_initialization(self, tau, dead_time, interlocking_time, kwargs): - super().test_initialization(tau, dead_time, interlocking_time, kwargs) - conv = self.class_to_test(tau=tau, dead_time=dead_time, interlocking_time=interlocking_time, **kwargs) + def test_initialization(self, tau, interlocking_time, kwargs): + super().test_initialization(tau, interlocking_time, kwargs) + conv = self.class_to_test(tau=tau, interlocking_time=interlocking_time, **kwargs) assert np.all( conv.action_space.low == np.concatenate([subconv.action_space.low for subconv in conv._sub_converters]) ), "Action space lower boundaries in the multi converter do not fit the subconverters." @@ -1093,7 +1090,6 @@ def test_initialization(self, tau, dead_time, interlocking_time, kwargs): ), "Current space dims in the multi converter do not fit the subconverters." for sc in conv._sub_converters: assert sc._interlocking_time == interlocking_time - assert sc._dead_time == dead_time assert sc._tau == tau def test_reset(self, converter): @@ -1142,26 +1138,25 @@ class TestFiniteB6BridgeConverter(TestFiniteConverter): def converter(self): conv = self.class_to_test() subconverters = [ - PowerElectronicConverterWrapper(subconverter, tau=conv._tau) for subconverter in conv._subconverters + PowerElectronicConverterWrapper(subconverter, tau=conv._tau) for subconverter in conv._sub_converters ] - conv._subconverters = subconverters + conv._sub_converters = subconverters return conv - @pytest.mark.parametrize("tau, dead_time, interlocking_time, kwargs", [ - (1, True, 0.1, {}), - (0.1, False, 0.0, {}), + @pytest.mark.parametrize("tau, interlocking_time, kwargs", [ + (1, 0.1, {}), + (0.1, 0.0, {}), ]) - def test_subconverter_initialization(self, tau, dead_time, interlocking_time, kwargs): - conv = self.class_to_test(tau=tau, dead_time=dead_time, interlocking_time=interlocking_time, **kwargs) - for sc in conv._subconverters: + def test_subconverter_initialization(self, tau, interlocking_time, kwargs): + conv = self.class_to_test(tau=tau, interlocking_time=interlocking_time, **kwargs) + for sc in conv._sub_converters: assert sc._interlocking_time == interlocking_time - assert sc._dead_time == dead_time assert sc._tau == tau def test_reset(self, converter): u_init = converter.reset() assert np.all( - subconv.reset_counter == 1 for subconv in converter._subconverters + subconv.reset_counter == 1 for subconv in converter._sub_converters ) assert u_init == [-0.5] * 3 @@ -1169,10 +1164,10 @@ def test_set_action(self, converter, *_): for action in range(converter.action_space.n): t = np.random.rand() converter.set_action(action, t) - assert converter._subconverters[0].last_t == t - assert converter._subconverters[1].last_t == t - assert converter._subconverters[2].last_t == t - subactions = [sc.last_action % 2 for sc in converter._subconverters] + assert converter._sub_converters[0].last_t == t + assert converter._sub_converters[1].last_t == t + assert converter._sub_converters[2].last_t == t + subactions = [sc.last_action % 2 for sc in converter._sub_converters] assert action == reduce(lambda x, y: 2*x+y, subactions) time = 0 @@ -1191,7 +1186,7 @@ def test_set_action(self, converter, *_): @pytest.mark.parametrize('i_out', [[-1, -1, 0], [1, 1, -2], [0, 0, 1]]) def test_convert(self, converter, i_out): t = np.random.rand() - sc1, sc2, sc3 = converter._subconverters + sc1, sc2, sc3 = converter._sub_converters for action in range(converter.action_space.n): converter.set_action(action, t) u_out = converter.convert(i_out, t) @@ -1203,7 +1198,7 @@ def test_convert(self, converter, i_out): @pytest.mark.parametrize('i_out', [[-1, -1, 0], [1, 1, -2], [0, 0, 1]]) def test_i_sup(self, converter, i_out): - sc1, sc2, sc3 = converter._subconverters + sc1, sc2, sc3 = converter._sub_converters for action in range(converter.action_space.n): converter.set_action(action, converter._tau * action) i_sup = converter.i_sup(i_out) diff --git a/tests/testing_utils.py b/tests/testing_utils.py index 6c669213..ff49b8bd 100644 --- a/tests/testing_utils.py +++ b/tests/testing_utils.py @@ -334,8 +334,8 @@ class DummyConverter(PowerElectronicConverter): currents = Box(-1, 1, shape=(1,), dtype=np.float64) action_space = Discrete(4) - def __init__(self, tau=2E-4, dead_time=False, interlocking_time=0, action_space=None, voltages=None, currents=None, **kwargs): - super().__init__(tau, dead_time, interlocking_time) + def __init__(self, tau=2E-4, interlocking_time=0, action_space=None, voltages=None, currents=None, **kwargs): + super().__init__(tau, interlocking_time) self.action_space = action_space or self.action_space self.voltages = voltages or self.voltages self.currents = currents or self.currents From 1682cd1838255b1d47ddda3a222bede067f7fc1f Mon Sep 17 00:00:00 2001 From: Arne Date: Fri, 21 Jan 2022 21:07:34 +0100 Subject: [PATCH 22/79] Test fixes and adaption of dq_to_abc_action_processor.py to DFIM --- .../physical_systems/converters.py | 4 +- .../physical_systems/physical_systems.py | 5 +-- .../dq_to_abc_action_processor.py | 33 ++++++++++++-- .../test_physical_systems/test_converters.py | 44 ++++++++++--------- 4 files changed, 57 insertions(+), 29 deletions(-) diff --git a/gym_electric_motor/physical_systems/converters.py b/gym_electric_motor/physical_systems/converters.py index bd1feb77..c3d3fe4f 100644 --- a/gym_electric_motor/physical_systems/converters.py +++ b/gym_electric_motor/physical_systems/converters.py @@ -33,7 +33,7 @@ def __init__(self, tau, interlocking_time=0.0): self._tau = tau self._interlocking_time = interlocking_time self._action_start_time = 0.0 - self._current_action = None + self._current_action = self._reset_action def reset(self): """ @@ -42,7 +42,7 @@ def reset(self): Returns: list(float): A default output voltage after reset(=0V). """ - self._current_action = None + self._current_action = self._reset_action self._action_start_time = 0.0 return [0.0] diff --git a/gym_electric_motor/physical_systems/physical_systems.py b/gym_electric_motor/physical_systems/physical_systems.py index 1154453a..f896175e 100644 --- a/gym_electric_motor/physical_systems/physical_systems.py +++ b/gym_electric_motor/physical_systems/physical_systems.py @@ -703,7 +703,6 @@ class DoublyFedInductionMotorSystem(ThreePhaseMotorSystem): def __init__(self, ode_solver='scipy.ode', **kwargs): """ Args: - control_space(str):('abc' or 'dq') Choose, if actions the actions space is in dq or abc space kwargs: Further arguments to pass tp SCMLSystem """ super().__init__(ode_solver=ode_solver, **kwargs) @@ -721,9 +720,7 @@ def __init__(self, ode_solver='scipy.ode', **kwargs): + self._converter.subsignal_voltage_space_dims[self.rotor_voltage_space_idx] def _set_limits(self): - """ - Method to set the physical limits from the modules. - """ + """Method to set the physical limits from the modules.""" for ind, state in enumerate(self._state_names): motor_lim = self._electrical_motor.limits.get(state, np.inf) mechanical_lim = self._mechanical_load.limits.get(state, np.inf) diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py index 5846c650..ead4e45a 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -11,7 +11,7 @@ class DqToAbcActionProcessor(StateActionProcessor): """ @property def action_space(self): - return gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) + return self._action_space @staticmethod def _transformation(action, angle): @@ -38,6 +38,7 @@ def __init__(self, angle_name=None, physical_system=None): self._state = None self._angle_name = angle_name self._angle_advance = 0.0 + self._action_space = gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) super().__init__(physical_system) def set_physical_system(self, physical_system): @@ -46,16 +47,21 @@ def set_physical_system(self, physical_system): 'The motor in the system has to derive from the ThreePhaseMotor to define transformations.' super().set_physical_system(physical_system) + if isinstance(physical_system, ps.DoublyFedInductionMotorSystem): + self._set_dfim_system(physical_system) + return self + # If no angle name was passed, try to use defaults. ('epsilon' for sync motors 'psi_angle' for induction motors) if self._angle_name is None: if isinstance(physical_system.electrical_motor, (ps.PermanentMagnetSynchronousMotor, ps.SynchronousReluctanceMotor)): self._angle_name = 'epsilon' - else: + elif isinstance(physical_system.electrical_motor, ps.SquirrelCageInductionMotor): self._angle_name = 'psi_angle' assert self._angle_name in physical_system.state_names, \ - f'Angle {self._angle_name} not in the states of the physical system. Probably, a flux observer is required.' + f'Angle {self._angle_name} not in the states of the physical system. Probably a flux observer is required.' + self._angle_index = physical_system.state_names.index(self._angle_name) self._omega_index = physical_system.state_names.index('omega') self._angle_advance = 0.5 @@ -63,10 +69,18 @@ def set_physical_system(self, physical_system): # If dead time has been added to the system increase the angle advance by the amount of dead time steps if hasattr(physical_system, 'dead_time'): self._angle_advance += physical_system.dead_time + self.simulate = self._single_angle_simulate return self + def _set_dfim_system(self, physical_system): + self._action_space = gym.spaces.Box(-1, 1, shape=(4,), dtype=np.float64) + raise NotImplementedError + def simulate(self, action): # Docstring of super class + raise ReferenceError('Physical System is still unset.') + + def _single_angle_simulate(self, action): advanced_angle = self._state[self._angle_index] \ + self._angle_advance * self._physical_system.tau * self._state[self._omega_index] abc_action = self._transformation(action, advanced_angle) @@ -74,6 +88,19 @@ def simulate(self, action): self._state = normalized_state * self._physical_system.limits return normalized_state + def _dfim_simulate(self, action): + # Docstring of super class + advanced_angle = self._state[self._angle_index] \ + + self._angle_advance * self._physical_system.tau * self._state[self._omega_index] + dq_action_stator = action[:3] + dq_action_rotor = action[3:] + abc_action_stator = self._transformation(dq_action_stator, advanced_angle) + abc_action_rotor = self._transformation(dq_action_rotor, self._angle_index[0] - advanced_angle) + abc_action = np.concatenate((abc_action_stator, abc_action_rotor)).tolist() + normalized_state = self._physical_system.simulate(abc_action) + self._state = normalized_state * self._physical_system.limits + return normalized_state + def reset(self, **kwargs): # Docstring of super class normalized_state = self._physical_system.reset() diff --git a/tests/test_physical_systems/test_converters.py b/tests/test_physical_systems/test_converters.py index ec6cecc3..10695061 100644 --- a/tests/test_physical_systems/test_converters.py +++ b/tests/test_physical_systems/test_converters.py @@ -482,18 +482,20 @@ def test_discrete_b6_bridge(): [1, 1, -1], [1, -1, -1], [1, -1, -1], - [1, -1, 1], + [1, -1, -1], [1, -1, 1], [-1, -1, 1], [-1, -1, 1], [-1, 1, -1], [-1, 1, -1], + [-1, 1, -1], [-1, 1, 1], - [-1, 1, 1], - [-1, 1, 1], + [1, 1, 1], [1, 1, 1], [-1, 1, -1], [-1, -1, -1], + [-1, -1, -1], + [1, -1, -1], [1, -1, -1]]) / 2 times = np.arange(len(actions)) * tau @@ -559,17 +561,18 @@ def test_continuous_b6_bridge(): assert abs(voltage[0] - single_action / 2) < 1E-9 # testing parametrized converter - expected_voltages = np.array([[-5, -4.95, -5], - [5, -4.95, 3.2], - [3.7, -4.8, -1.55], - [-1.2, 4.85, -5], - [3.3, 2.45, -4.7], - [-1.55, 2.45, 4.85], - [-5, 4.95, 2.55], - [-4.8, 3.7, 2.55], - [4.85, -1.2, 4.95], - [2.45, 3.2, 3.7]]) / 10 - + expected_voltages = np.array([ + [0.495, -0.495, 0.32], + [0.38, -0.47, -0.155], + [-0.13,0.485, -0.5], + [0.33,0.245, -0.48], + [-0.145, 0.245, 0.495], + [-0.5, 0.495, 0.245], + [-0.48, 0.37, 0.255], + [0.485, -0.13, 0.5], + [0.245, 0.33, 0.37], + [0.245, -0.155, -0.13] + ]) converter_init_1 = cv.ContB6BridgeConverter(**cf.converter_parameter) converter_init_2 = make_module(cv.PowerElectronicConverter, 'Cont-B6C', **cf.converter_parameter) converters = [converter_init_1, converter_init_2] @@ -592,6 +595,7 @@ def test_continuous_b6_bridge(): for voltage, test_voltage in zip(voltages, expected_voltage): assert abs(voltage - test_voltage) < 1E-9 + # endregion @@ -614,8 +618,8 @@ def converter(self): (1, 0.1), (0.1, 0.0), ]) - def test_initialization(self, tau, interlocking_time): - converter = self.class_to_test(tau=tau, interlocking_time=interlocking_time) + def test_initialization(self, tau, interlocking_time, **kwargs): + converter = self.class_to_test(tau=tau, interlocking_time=interlocking_time, **kwargs) assert converter._tau == tau assert converter._interlocking_time == interlocking_time @@ -757,13 +761,13 @@ def test_set_switching_pattern(self, monkeypatch, converter, interlocking_time): monkeypatch.setattr(converter, "_switching_state", 1) monkeypatch.setattr(converter, "_current_action", 1) monkeypatch.setattr(converter, "_action_start_time", 1) - assert converter._set_switching_pattern() == [converter._tau + 1] + assert converter._set_switching_pattern(1) == [converter._tau + 1] assert converter._switching_pattern == [converter._current_action] # Test if interlocking step is required, if action changes to another <> 0 monkeypatch.setattr(converter, "_switching_state", 1) monkeypatch.setattr(converter, "_current_action", 2) monkeypatch.setattr(converter, "_action_start_time", 2) - switching_times = converter._set_switching_pattern() + switching_times = converter._set_switching_pattern(2) if interlocking_time > 0: assert switching_times == [interlocking_time + 2, converter._tau + 2] assert converter._switching_pattern == [0, converter._current_action] @@ -978,7 +982,7 @@ def converter(self): (0.1, 0.0, {'subconverters': ['Finite-1QC', 'Finite-B6C', 'Finite-4QC']}), ]) def test_initialization(self, tau, interlocking_time, kwargs): - super().test_initialization(tau, interlocking_time, kwargs) + super().test_initialization(tau, interlocking_time, **kwargs) conv = self.class_to_test(tau=tau, interlocking_time=interlocking_time, **kwargs) assert np.all( conv.subsignal_voltage_space_dims == @@ -1072,7 +1076,7 @@ def test_registered(self): (0.1, 0.0, {'subconverters': ['Cont-1QC', 'Cont-B6C', 'Cont-4QC']}), ]) def test_initialization(self, tau, interlocking_time, kwargs): - super().test_initialization(tau, interlocking_time, kwargs) + super().test_initialization(tau, interlocking_time, **kwargs) conv = self.class_to_test(tau=tau, interlocking_time=interlocking_time, **kwargs) assert np.all( conv.action_space.low == np.concatenate([subconv.action_space.low for subconv in conv._sub_converters]) From 9cb06c8505bc3b091c010b338a5986bf4d3b930a Mon Sep 17 00:00:00 2001 From: Arne Date: Fri, 21 Jan 2022 21:25:22 +0100 Subject: [PATCH 23/79] Test fix --- .../state_action_processors/dq_to_abc_action_processor.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py index ead4e45a..4413a7f7 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -9,9 +9,6 @@ class DqToAbcActionProcessor(StateActionProcessor): """The DqToAbcActionProcessor converts an inner system with an AC motor and actions in abc coordinates to a system to which actions in the dq-coordinate system can be applied. """ - @property - def action_space(self): - return self._action_space @staticmethod def _transformation(action, angle): @@ -38,9 +35,10 @@ def __init__(self, angle_name=None, physical_system=None): self._state = None self._angle_name = angle_name self._angle_advance = 0.0 - self._action_space = gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) + super().__init__(physical_system) + def set_physical_system(self, physical_system): # Docstring of super class assert isinstance(physical_system.electrical_motor, ps.ThreePhaseMotor), \ @@ -50,7 +48,7 @@ def set_physical_system(self, physical_system): if isinstance(physical_system, ps.DoublyFedInductionMotorSystem): self._set_dfim_system(physical_system) return self - + self._action_space = gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) # If no angle name was passed, try to use defaults. ('epsilon' for sync motors 'psi_angle' for induction motors) if self._angle_name is None: if isinstance(physical_system.electrical_motor, From 3c2f9d59ac5897f4dcc7ffd1cac2e5aaef255fa4 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 24 Jan 2022 20:23:28 +0100 Subject: [PATCH 24/79] DFIM DqToAbcProcessor compatibility --- .../cont_cc_dfim_env.py | 8 ++- .../cont_sc_dfim_env.py | 10 +++- .../cont_tc_dfim_env.py | 10 +++- .../finite_cc_dfim_env.py | 8 ++- .../finite_sc_dfim_env.py | 8 ++- .../finite_tc_dfim_env.py | 8 ++- .../electric_motors/three_phase_motor.py | 3 +- .../dq_to_abc_action_processor.py | 55 ++++++++++--------- 8 files changed, 69 insertions(+), 41 deletions(-) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_cc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_cc_dfim_env.py index d17681c0..bebb81ce 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_cc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_cc_dfim_env.py @@ -94,7 +94,8 @@ class ContCurrentControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): def __init__( self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4 + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=() ): """ Args: @@ -116,6 +117,8 @@ def __init__( tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -170,5 +173,6 @@ def __init__( ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_sc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_sc_dfim_env.py index db93d10b..9e7042a8 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_sc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_sc_dfim_env.py @@ -91,9 +91,10 @@ class ContSpeedControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -114,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -159,5 +162,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_tc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_tc_dfim_env.py index 2e29b144..dd233d32 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_tc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_tc_dfim_env.py @@ -91,9 +91,10 @@ class ContTorqueControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -114,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -163,5 +166,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_cc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_cc_dfim_env.py index 1eaeb61f..5daeb853 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_cc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_cc_dfim_env.py @@ -93,7 +93,8 @@ class FiniteCurrentControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -114,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -168,5 +171,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_sc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_sc_dfim_env.py index a32c7aa4..c03e0716 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_sc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_sc_dfim_env.py @@ -93,7 +93,8 @@ class FiniteSpeedControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -114,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -159,5 +162,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_tc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_tc_dfim_env.py index 63672139..00e3126c 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_tc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_tc_dfim_env.py @@ -93,7 +93,8 @@ class FiniteTorqueControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, + state_action_processors=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -114,6 +115,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () + state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: All parameters of type env-arg can be selected as one of the following types: @@ -157,5 +160,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, + state_action_processors=state_action_processors ) diff --git a/gym_electric_motor/physical_systems/electric_motors/three_phase_motor.py b/gym_electric_motor/physical_systems/electric_motors/three_phase_motor.py index 7dd72301..b558097a 100644 --- a/gym_electric_motor/physical_systems/electric_motors/three_phase_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/three_phase_motor.py @@ -64,8 +64,7 @@ def q(quantities, epsilon): """ cos = math.cos(epsilon) sin = math.sin(epsilon) - return cos * quantities[0] - sin * quantities[1], sin * quantities[ - 0] + cos * quantities[1] + return cos * quantities[0] - sin * quantities[1], sin * quantities[0] + cos * quantities[1] @staticmethod def q_inv(quantities, epsilon): diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py index 4413a7f7..b9886385 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -38,45 +38,50 @@ def __init__(self, angle_name=None, physical_system=None): super().__init__(physical_system) - def set_physical_system(self, physical_system): # Docstring of super class assert isinstance(physical_system.electrical_motor, ps.ThreePhaseMotor), \ 'The motor in the system has to derive from the ThreePhaseMotor to define transformations.' super().set_physical_system(physical_system) - - if isinstance(physical_system, ps.DoublyFedInductionMotorSystem): - self._set_dfim_system(physical_system) - return self - self._action_space = gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) - # If no angle name was passed, try to use defaults. ('epsilon' for sync motors 'psi_angle' for induction motors) - if self._angle_name is None: - if isinstance(physical_system.electrical_motor, - (ps.PermanentMagnetSynchronousMotor, ps.SynchronousReluctanceMotor)): - self._angle_name = 'epsilon' - elif isinstance(physical_system.electrical_motor, ps.SquirrelCageInductionMotor): - self._angle_name = 'psi_angle' - - assert self._angle_name in physical_system.state_names, \ - f'Angle {self._angle_name} not in the states of the physical system. Probably a flux observer is required.' - - self._angle_index = physical_system.state_names.index(self._angle_name) self._omega_index = physical_system.state_names.index('omega') + if isinstance(physical_system.unwrapped, ps.DoublyFedInductionMotorSystem): + self._set_dfim_system(physical_system) + else: + self._action_space = gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) + # If no angle name was passed, try to use defaults. + # ('epsilon' for sync motors 'psi_angle' for induction motors) + if self._angle_name is None: + if isinstance( + physical_system.electrical_motor, + (ps.PermanentMagnetSynchronousMotor, ps.SynchronousReluctanceMotor) + ): + self._angle_name = 'epsilon' + elif isinstance(physical_system.electrical_motor, ps.SquirrelCageInductionMotor): + self._angle_name = 'psi_angle' + self.simulate = self._single_angle_simulate + self._angle_index = physical_system.state_names.index(self._angle_name) + assert self._angle_name in physical_system.state_names, \ + f'Angle {self._angle_name} not in the states of the physical system. ' \ + f'Probably a flux observer is required.' + self._angle_advance = 0.5 # If dead time has been added to the system increase the angle advance by the amount of dead time steps if hasattr(physical_system, 'dead_time'): self._angle_advance += physical_system.dead_time - self.simulate = self._single_angle_simulate + return self def _set_dfim_system(self, physical_system): self._action_space = gym.spaces.Box(-1, 1, shape=(4,), dtype=np.float64) - raise NotImplementedError + self._angle_index = [ + physical_system.state_names.index('psi_angle'), physical_system.state_names.index('epsilon') + ] + self.simulate = self._dfim_simulate def simulate(self, action): # Docstring of super class - raise ReferenceError('Physical System is still unset.') + raise ReferenceError('Physical System is unset.') def _single_angle_simulate(self, action): advanced_angle = self._state[self._angle_index] \ @@ -88,13 +93,13 @@ def _single_angle_simulate(self, action): def _dfim_simulate(self, action): # Docstring of super class - advanced_angle = self._state[self._angle_index] \ + advanced_angle = self._state[self._angle_index[1]] \ + self._angle_advance * self._physical_system.tau * self._state[self._omega_index] - dq_action_stator = action[:3] - dq_action_rotor = action[3:] + dq_action_stator = action[:2] + dq_action_rotor = action[2:] abc_action_stator = self._transformation(dq_action_stator, advanced_angle) abc_action_rotor = self._transformation(dq_action_rotor, self._angle_index[0] - advanced_angle) - abc_action = np.concatenate((abc_action_stator, abc_action_rotor)).tolist() + abc_action = np.concatenate((abc_action_stator, abc_action_rotor)) normalized_state = self._physical_system.simulate(abc_action) self._state = normalized_state * self._physical_system.limits return normalized_state From 11b2cdae5bae975d7ff6abcd96f045c7bad7f78b Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 24 Jan 2022 20:41:04 +0100 Subject: [PATCH 25/79] Example change rollbacks --- .../classic_controllers_dc_motor_example.py | 4 ++-- .../classic_controllers_ind_motor_example.py | 8 ++++---- .../classic_controllers_synch_motor_example.py | 6 +++--- .../dq_to_abc_action_processor.py | 15 ++++++++++++++- .../state_action_processors/flux_observer.py | 7 ++++--- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/examples/classic_controllers/classic_controllers_dc_motor_example.py b/examples/classic_controllers/classic_controllers_dc_motor_example.py index c0e6a458..d8767cc5 100644 --- a/examples/classic_controllers/classic_controllers_dc_motor_example.py +++ b/examples/classic_controllers/classic_controllers_dc_motor_example.py @@ -20,8 +20,8 @@ """ motor_type = 'PermExDc' - control_type = 'SC' - action_type = 'F' + control_type = 'TC' + action_type = 'Cont' motor = action_type + '-' + control_type + '-' + motor_type + '-v0' diff --git a/examples/classic_controllers/classic_controllers_ind_motor_example.py b/examples/classic_controllers/classic_controllers_ind_motor_example.py index ab957c7e..13aebbc1 100644 --- a/examples/classic_controllers/classic_controllers_ind_motor_example.py +++ b/examples/classic_controllers/classic_controllers_ind_motor_example.py @@ -25,10 +25,10 @@ env_id = action_type + '-' + control_type + '-' + motor_type + '-v0' # definition of the plotted variables - #states = ['omega', 'torque', 'i_sd', 'i_sq', 'u_sd', 'u_sq'] - #external_ref_plots = [ExternallyReferencedStatePlot(state) for state in states] - #external_plot = [ExternalPlot(referenced=control_type != 'CC'), ExternalPlot(min=-np.pi, max=np.pi)] - #external_ref_plots += external_plot + states = ['omega', 'torque', 'i_sd', 'i_sq', 'u_sd', 'u_sq'] + external_ref_plots = [ExternallyReferencedStatePlot(state) for state in states] + external_plot = [ExternalPlot(referenced=control_type != 'CC'), ExternalPlot(min=-np.pi, max=np.pi)] + external_ref_plots += external_plot # initialize the gym-electric-motor environment env = gem.make(env_id, state_action_processors=(FluxObserver(),), diff --git a/examples/classic_controllers/classic_controllers_synch_motor_example.py b/examples/classic_controllers/classic_controllers_synch_motor_example.py index 388d6e2b..091c5721 100644 --- a/examples/classic_controllers/classic_controllers_synch_motor_example.py +++ b/examples/classic_controllers/classic_controllers_synch_motor_example.py @@ -18,9 +18,9 @@ 'Finite' Discrete Action Space """ - motor_type = 'SynRM' - control_type = 'SC' - action_type = 'Finite' + motor_type = 'PMSM' + control_type = 'TC' + action_type = 'Cont' env_id = action_type + '-' + control_type + '-' + motor_type + '-v0' diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py index b9886385..ab93cf53 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -84,6 +84,13 @@ def simulate(self, action): raise ReferenceError('Physical System is unset.') def _single_angle_simulate(self, action): + """Dq to abc space transformation function for all environments except those for a doubly fed induction motor. + + Args: + action: The actions for the stator and rotor circuit in dq-coordinates. + Returns: + The next state of the physical system. + """ advanced_angle = self._state[self._angle_index] \ + self._angle_advance * self._physical_system.tau * self._state[self._omega_index] abc_action = self._transformation(action, advanced_angle) @@ -92,7 +99,13 @@ def _single_angle_simulate(self, action): return normalized_state def _dfim_simulate(self, action): - # Docstring of super class + """Dq to abc space transformation function for doubly fed induction motor environments. + + Args: + action: The actions for the stator and rotor circuit in dq-coordinates. + Returns: + The next state of the physical system. + """ advanced_angle = self._state[self._angle_index[1]] \ + self._angle_advance * self._physical_system.tau * self._state[self._omega_index] dq_action_stator = action[:2] diff --git a/gym_electric_motor/state_action_processors/flux_observer.py b/gym_electric_motor/state_action_processors/flux_observer.py index 7402b7f3..9f446089 100644 --- a/gym_electric_motor/state_action_processors/flux_observer.py +++ b/gym_electric_motor/state_action_processors/flux_observer.py @@ -6,11 +6,12 @@ class FluxObserver(StateActionProcessor): - """The FluxObserver extends the systems state vector by estimated flux states ``psi_abs``, and ``psi_angle``. + """The FluxObserver extends the systems state vector of induction machine environments by estimated flux states + ``psi_abs``, and ``psi_angle``. The flux is estimated as follows: - .. math:: psi_{abs} = |\Psi | + .. math:: psi_{abs} = |\Psi| - .. math:: psi_{angle} = \\angle{\Psi_t} + .. math:: psi_{angle} = \\angle{\Psi} .. math:: \Psi \in \mathbb{C} From ac68b69985ac94dfd1998efbff0e12c7211ea36a Mon Sep 17 00:00:00 2001 From: Arne Date: Tue, 25 Jan 2022 19:55:42 +0100 Subject: [PATCH 26/79] Matplotlib widget and visual studio code compatibility --- .../environment_features/GEM_cookbook.ipynb | 189 +++++++++--------- .../visualization/motor_dashboard.py | 7 +- 2 files changed, 93 insertions(+), 103 deletions(-) diff --git a/examples/environment_features/GEM_cookbook.ipynb b/examples/environment_features/GEM_cookbook.ipynb index a5c30366..38e205f3 100644 --- a/examples/environment_features/GEM_cookbook.ipynb +++ b/examples/environment_features/GEM_cookbook.ipynb @@ -148,19 +148,30 @@ "### 1.4 Basic Routine:" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "Note: \n", + "If the notebook is executed in Colab, the visualization cannot be used because dynamic matplotlib backends are not supported.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When the environment is run in a jupiter notebook, it is recommended to split the enviromnent creation and execution into two cells. Furthermore, the ``visu.initialize()`` call is required. The dashboard is per default the first (and only) visualization in an environment.\n", + "\n", + "If the environment is run from a script, the ``visu.initialize()`` call is not necessary." + ] + }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\arnet\\anaconda3\\envs\\gem_dev\\lib\\site-packages\\gym\\logger.py:30: UserWarning: \u001b[33mWARN: Box bound precision lowered by casting to float32\u001b[0m\n", - " warnings.warn(colorize('%s: %s'%('WARN', msg % args), 'yellow'))\n" - ] - }, { "data": { "application/javascript": [ @@ -229,7 +240,9 @@ " fig.send_message('supports_binary', { value: fig.supports_binary });\n", " fig.send_message('send_image_mode', {});\n", " if (fig.ratio !== 1) {\n", - " fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", " }\n", " fig.send_message('refresh', {});\n", " };\n", @@ -608,22 +621,7 @@ "};\n", "\n", "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", - " var cursor = msg['cursor'];\n", - " switch (cursor) {\n", - " case 0:\n", - " cursor = 'pointer';\n", - " break;\n", - " case 1:\n", - " cursor = 'default';\n", - " break;\n", - " case 2:\n", - " cursor = 'crosshair';\n", - " break;\n", - " case 3:\n", - " cursor = 'move';\n", - " break;\n", - " }\n", - " fig.rubberband_canvas.style.cursor = cursor;\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", "};\n", "\n", "mpl.figure.prototype.handle_message = function (fig, msg) {\n", @@ -735,7 +733,7 @@ " };\n", "};\n", "\n", - "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", "mpl.findpos = function (e) {\n", " //this section is from http://www.quirksmode.org/js/events_properties.html\n", " var targ;\n", @@ -763,7 +761,7 @@ "/*\n", " * return a copy of an object with only non-object keys\n", " * we need this to avoid circular references\n", - " * http://stackoverflow.com/a/24161582/3208463\n", + " * https://stackoverflow.com/a/24161582/3208463\n", " */\n", "function simpleKeys(original) {\n", " return Object.keys(original).reduce(function (obj, key) {\n", @@ -852,7 +850,7 @@ "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", "// prettier-ignore\n", "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", - "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", "\n", "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", "\n", @@ -1086,11 +1084,6 @@ "};\n", "\n", "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", - " var manager = IPython.notebook.keyboard_manager;\n", - " if (!manager) {\n", - " manager = IPython.keyboard_manager;\n", - " }\n", - "\n", " // Check for shift+enter\n", " if (event.shiftKey && event.which === 13) {\n", " this.canvas_div.blur();\n", @@ -1147,7 +1140,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -1158,26 +1151,36 @@ } ], "source": [ - "# Matplotlib has to use the notebook-backend, when the code is run inside a jupyter notebook\n", "%matplotlib notebook\n", + "# Alternatively for Visual Studio Code use:\n", + "#%matplotlib widget\n", "import gym_electric_motor as gem\n", + "# instantiate a finite control set current controlled PMSM\n", + "env = gem.make(\"Finite-CC-PMSM-v0\") \n", + "visu = env.visualizations[0]\n", + "visu.initialize()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "done = True\n", "\n", - "if __name__ == '__main__':\n", - " # instantiate a finite control set current controlled PMSM\n", - " env = gem.make(\"Finite-CC-PMSM-v0\") \n", - " done = True\n", - " for _ in range(10000):\n", - " if done:\n", - " # Reset the environment\n", - " # This is required initally or after an episode end due to a constraint violation in the env.\n", - " state, references = env.reset()\n", - " # visualize environment. Red vertical lines indicate a constraint violation and therefore, a reset environment.\n", - " env.render() \n", - " # pick random control actions\n", - " action = env.action_space.sample()\n", - " # Execute one control cycle on the environment\n", - " (states, references), rewards, done, _ = env.step(action) \n", - " env.close()" + "for _ in range(10000):\n", + " if done:\n", + " # Reset the environment\n", + " # This is required initally or after an episode end due to a constraint violation in the env.\n", + " state, references = env.reset()\n", + " # visualize environment. Red vertical lines indicate a constraint violation and therefore, a reset environment.\n", + " # Blue vertical lines indicate an additional reset by the user which is not due to a terminated episode.\n", + " env.render() \n", + " # pick random control actions\n", + " action = env.action_space.sample()\n", + " # Execute one control cycle on the environment\n", + " (states, references), rewards, done, _ = env.step(action) " ] }, { @@ -1560,7 +1563,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -1598,7 +1601,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -1644,18 +1647,9 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\arnet\\anaconda3\\envs\\gem_dev\\lib\\site-packages\\gym\\logger.py:30: UserWarning: \u001b[33mWARN: Box bound precision lowered by casting to float32\u001b[0m\n", - " warnings.warn(colorize('%s: %s'%('WARN', msg % args), 'yellow'))\n" - ] - } - ], + "outputs": [], "source": [ "# define a PMSM with discrete action space\n", "env = gem.make( \n", @@ -1694,7 +1688,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -1733,7 +1727,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -1804,7 +1798,9 @@ " fig.send_message('supports_binary', { value: fig.supports_binary });\n", " fig.send_message('send_image_mode', {});\n", " if (fig.ratio !== 1) {\n", - " fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", " }\n", " fig.send_message('refresh', {});\n", " };\n", @@ -2183,22 +2179,7 @@ "};\n", "\n", "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", - " var cursor = msg['cursor'];\n", - " switch (cursor) {\n", - " case 0:\n", - " cursor = 'pointer';\n", - " break;\n", - " case 1:\n", - " cursor = 'default';\n", - " break;\n", - " case 2:\n", - " cursor = 'crosshair';\n", - " break;\n", - " case 3:\n", - " cursor = 'move';\n", - " break;\n", - " }\n", - " fig.rubberband_canvas.style.cursor = cursor;\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", "};\n", "\n", "mpl.figure.prototype.handle_message = function (fig, msg) {\n", @@ -2310,7 +2291,7 @@ " };\n", "};\n", "\n", - "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", "mpl.findpos = function (e) {\n", " //this section is from http://www.quirksmode.org/js/events_properties.html\n", " var targ;\n", @@ -2338,7 +2319,7 @@ "/*\n", " * return a copy of an object with only non-object keys\n", " * we need this to avoid circular references\n", - " * http://stackoverflow.com/a/24161582/3208463\n", + " * https://stackoverflow.com/a/24161582/3208463\n", " */\n", "function simpleKeys(original) {\n", " return Object.keys(original).reduce(function (obj, key) {\n", @@ -2427,7 +2408,7 @@ "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", "// prettier-ignore\n", "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", - "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", "\n", "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", "\n", @@ -2661,11 +2642,6 @@ "};\n", "\n", "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", - " var manager = IPython.notebook.keyboard_manager;\n", - " if (!manager) {\n", - " manager = IPython.keyboard_manager;\n", - " }\n", - "\n", " // Check for shift+enter\n", " if (event.shiftKey && event.which === 13) {\n", " this.canvas_div.blur();\n", @@ -2722,7 +2698,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -2733,13 +2709,26 @@ } ], "source": [ - "%matplotlib notebook\n", - "\n", - "# This exta-call is necessary in jupyter notebooks\n", - "# and only if the MotorDashboard is selected as visualization which is the default visualizer.\n", - "# It resets the figures. Otherwise the figures cannot be displayed properly when a cell is called twice, for example.\n", - "env.visualizations[0].reset_figures()\n", - "\n", + "visualization.initialize()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\arnet\\AppData\\Roaming\\Python\\Python39\\site-packages\\numpy\\core\\fromnumeric.py:3419: RuntimeWarning: Mean of empty slice.\n", + " return _methods._mean(a, axis=axis, dtype=dtype,\n", + "C:\\Users\\arnet\\AppData\\Roaming\\Python\\Python39\\site-packages\\numpy\\core\\_methods.py:188: RuntimeWarning: invalid value encountered in double_scalars\n", + " ret = ret.dtype.type(ret / rcount)\n" + ] + } + ], + "source": [ "# simple routine with random actions applied\n", "env.reset()\n", "k = 0\n", diff --git a/gym_electric_motor/visualization/motor_dashboard.py b/gym_electric_motor/visualization/motor_dashboard.py index 677565c3..e98c495c 100644 --- a/gym_electric_motor/visualization/motor_dashboard.py +++ b/gym_electric_motor/visualization/motor_dashboard.py @@ -136,7 +136,7 @@ def render(self): """Updates the plots every *update cycle* calls of this method.""" if not (self._time_plot_figure or self._episodic_plot_figure or self._step_plot_figure) \ and len(self._plots) > 0: - self._initialize() + self.initialize() if self._update_render: self._update() self._update_render = False @@ -196,12 +196,12 @@ def reset_figures(self): self._episodic_plot_figure = self._time_plot_figure = self._step_plot_figure = None self._figures = [] - def _initialize(self): + def initialize(self): """Called with first render() call to setup the figures and plots.""" plt.close() self._figures = [] - if plt.get_backend() == 'nbAgg': + if plt.get_backend() in ['nbAgg', 'module://ipympl.backend_nbagg']: self._initialize_figures_notebook() else: self._initialize_figures_window() @@ -277,3 +277,4 @@ def _update(self): for fig in self._figures: fig.canvas.draw() fig.canvas.flush_events() + From a11602d0b4f8fd9a6751e6ac6c86dc0de59a118a Mon Sep 17 00:00:00 2001 From: Arne Date: Tue, 25 Jan 2022 20:01:49 +0100 Subject: [PATCH 27/79] Matplotlib widget and visual studio code compatibility --- .../pmsm_mpc_dq_current_control.ipynb | 782 +------ .../keras_rl2_dqn_disc_pmsm_example.ipynb | 998 +-------- ...ble_baselines3_dqn_disc_pmsm_example.ipynb | 1973 +---------------- 3 files changed, 57 insertions(+), 3696 deletions(-) diff --git a/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb b/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb index 07f8bd99..dbbfff9b 100644 --- a/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb +++ b/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb @@ -313,774 +313,7 @@ "outputs": [ { "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "window.mpl = {};\n", - "\n", - "\n", - "mpl.get_websocket_type = function() {\n", - " if (typeof(WebSocket) !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof(MozWebSocket) !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert('Your browser does not have WebSocket support. ' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.');\n", - " };\n", - "}\n", - "\n", - "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = (this.ws.binaryType != undefined);\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById(\"mpl-warnings\");\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent = (\n", - " \"This browser does not support binary websocket messages. \" +\n", - " \"Performance may be slow.\");\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = $('
');\n", - " this._root_extra_style(this.root)\n", - " this.root.attr('style', 'display: inline-block');\n", - "\n", - " $(parent_element).append(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", - " fig.send_message(\"send_image_mode\", {});\n", - " if (mpl.ratio != 1) {\n", - " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", - " }\n", - " fig.send_message(\"refresh\", {});\n", - " }\n", - "\n", - " this.imageObj.onload = function() {\n", - " if (fig.image_mode == 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function() {\n", - " fig.ws.close();\n", - " }\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "}\n", - "\n", - "mpl.figure.prototype._init_header = function() {\n", - " var titlebar = $(\n", - " '
');\n", - " var titletext = $(\n", - " '
');\n", - " titlebar.append(titletext)\n", - " this.root.append(titlebar);\n", - " this.header = titletext[0];\n", - "}\n", - "\n", - "\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._init_canvas = function() {\n", - " var fig = this;\n", - "\n", - " var canvas_div = $('
');\n", - "\n", - " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", - "\n", - " function canvas_keyboard_event(event) {\n", - " return fig.key_event(event, event['data']);\n", - " }\n", - "\n", - " canvas_div.keydown('key_press', canvas_keyboard_event);\n", - " canvas_div.keyup('key_release', canvas_keyboard_event);\n", - " this.canvas_div = canvas_div\n", - " this._canvas_extra_style(canvas_div)\n", - " this.root.append(canvas_div);\n", - "\n", - " var canvas = $('');\n", - " canvas.addClass('mpl-canvas');\n", - " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", - "\n", - " this.canvas = canvas[0];\n", - " this.context = canvas[0].getContext(\"2d\");\n", - "\n", - " var backingStore = this.context.backingStorePixelRatio ||\n", - "\tthis.context.webkitBackingStorePixelRatio ||\n", - "\tthis.context.mozBackingStorePixelRatio ||\n", - "\tthis.context.msBackingStorePixelRatio ||\n", - "\tthis.context.oBackingStorePixelRatio ||\n", - "\tthis.context.backingStorePixelRatio || 1;\n", - "\n", - " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband = $('');\n", - " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", - "\n", - " var pass_mouse_events = true;\n", - "\n", - " canvas_div.resizable({\n", - " start: function(event, ui) {\n", - " pass_mouse_events = false;\n", - " },\n", - " resize: function(event, ui) {\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " stop: function(event, ui) {\n", - " pass_mouse_events = true;\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " });\n", - "\n", - " function mouse_event_fn(event) {\n", - " if (pass_mouse_events)\n", - " return fig.mouse_event(event, event['data']);\n", - " }\n", - "\n", - " rubberband.mousedown('button_press', mouse_event_fn);\n", - " rubberband.mouseup('button_release', mouse_event_fn);\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband.mousemove('motion_notify', mouse_event_fn);\n", - "\n", - " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", - " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", - "\n", - " canvas_div.on(\"wheel\", function (event) {\n", - " event = event.originalEvent;\n", - " event['data'] = 'scroll'\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " mouse_event_fn(event);\n", - " });\n", - "\n", - " canvas_div.append(canvas);\n", - " canvas_div.append(rubberband);\n", - "\n", - " this.rubberband = rubberband;\n", - " this.rubberband_canvas = rubberband[0];\n", - " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", - " this.rubberband_context.strokeStyle = \"#000000\";\n", - "\n", - " this._resize_canvas = function(width, height) {\n", - " // Keep the size of the canvas, canvas container, and rubber band\n", - " // canvas in synch.\n", - " canvas_div.css('width', width)\n", - " canvas_div.css('height', height)\n", - "\n", - " canvas.attr('width', width * mpl.ratio);\n", - " canvas.attr('height', height * mpl.ratio);\n", - " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", - "\n", - " rubberband.attr('width', width);\n", - " rubberband.attr('height', height);\n", - " }\n", - "\n", - " // Set the figure to an initial 600x600px, this will subsequently be updated\n", - " // upon first draw.\n", - " this._resize_canvas(600, 600);\n", - "\n", - " // Disable right mouse context menu.\n", - " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", - " return false;\n", - " });\n", - "\n", - " function set_focus () {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "}\n", - "\n", - "mpl.figure.prototype._init_toolbar = function() {\n", - " var fig = this;\n", - "\n", - " var nav_element = $('
');\n", - " nav_element.attr('style', 'width: 100%');\n", - " this.root.append(nav_element);\n", - "\n", - " // Define a callback function for later on.\n", - " function toolbar_event(event) {\n", - " return fig.toolbar_button_onclick(event['data']);\n", - " }\n", - " function toolbar_mouse_event(event) {\n", - " return fig.toolbar_button_onmouseover(event['data']);\n", - " }\n", - "\n", - " for(var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " // put a spacer in here.\n", - " continue;\n", - " }\n", - " var button = $('');\n button.click(method_name, toolbar_event);\n button.mouseover(tooltip, toolbar_mouse_event);\n nav_element.append(button);\n }\n\n // Add the status bar.\n var status_bar = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var button = $('');\n button.click(function (evt) { fig.handle_close(fig, {}); } );\n button.mouseover('Stop Interaction', toolbar_mouse_event);\n buttongrp.append(button);\n var titlebar = this.root.find($('.ui-dialog-titlebar'));\n titlebar.prepend(buttongrp);\n}\n\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.figure.prototype._canvas_extra_style = function(el){\n // this is important to make the div 'focusable\n el.attr('tabindex', 0)\n // reach out to IPython and tell the keyboard manager to turn it's self\n // off when our div gets focus\n\n // location in version 3\n if (IPython.notebook.keyboard_manager) {\n IPython.notebook.keyboard_manager.register_events(el);\n }\n else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n\n}\n\nmpl.figure.prototype._key_event_extra = function(event, name) {\n var manager = IPython.notebook.keyboard_manager;\n if (!manager)\n manager = IPython.keyboard_manager;\n\n // Check for shift+enter\n if (event.shiftKey && event.which == 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n}\n\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.find_output_cell = function(html_output) {\n // Return the cell and output element which can be found *uniquely* in the notebook.\n // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n // IPython event is triggered only after the cells have been serialised, which for\n // our purposes (turning an active figure into a static one), is too late.\n var cells = IPython.notebook.get_cells();\n var ncells = cells.length;\n for (var i=0; i= 3 moved mimebundle to data attribute of output\n data = data.data;\n }\n if (data['text/html'] == html_output) {\n return [cell, data, j];\n }\n }\n }\n }\n}\n\n// Register the function which deals with the matplotlib target/channel.\n// The kernel may be null if the page has been refreshed.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n", "text/plain": [ "" ] @@ -1110,9 +343,9 @@ ], "source": [ "%matplotlib notebook\n", - "\n", + "visu = MotorDashboard(state_plots=['i_sq', 'i_sd', 'u_sq', 'u_sd'], update_interval=10), # visualization\n", "env = gem.make('AbcCont-CC-PMSM-v0',\n", - " visualization=MotorDashboard(state_plots=['i_sq', 'i_sd', 'u_sq', 'u_sd'], update_interval=10), # visualization\n", + " visualization=visu\n", " load=ConstantSpeedLoad(omega_fixed=1000 * np.pi / 30), # Fixed preset speed\n", " ode_solver='scipy.solve_ivp', # ODE Solver of the simulation\n", " reference_generator=reference_generator, # initialize the reference generators\n", @@ -1132,6 +365,15 @@ " )\n", " )\n", "\n", + "visu.initialize()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "controller = Controller.make('mpc', env, ph=3) # initializing the MPC Controller\n", "state, reference = env.reset()\n", "cum_rew = 0\n", diff --git a/examples/reinforcement_learning_controllers/keras_rl2_dqn_disc_pmsm_example.ipynb b/examples/reinforcement_learning_controllers/keras_rl2_dqn_disc_pmsm_example.ipynb index 797fe136..590b32bb 100644 --- a/examples/reinforcement_learning_controllers/keras_rl2_dqn_disc_pmsm_example.ipynb +++ b/examples/reinforcement_learning_controllers/keras_rl2_dqn_disc_pmsm_example.ipynb @@ -110,6 +110,8 @@ } ], "source": [ + "%matplotlib notebook\n", + "# Use %matplotlib widget in Visual Studio Code\n", "import numpy as np\n", "from pathlib import Path\n", "import gym_electric_motor as gem\n", @@ -400,6 +402,15 @@ ")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "motor_dashboard.initialize()" + ] + }, { "cell_type": "code", "execution_count": 8, @@ -424,980 +435,7 @@ }, { "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "/* global mpl */\n", - "window.mpl = {};\n", - "\n", - "mpl.get_websocket_type = function () {\n", - " if (typeof WebSocket !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof MozWebSocket !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert(\n", - " 'Your browser does not have WebSocket support. ' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.'\n", - " );\n", - " }\n", - "};\n", - "\n", - "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = this.ws.binaryType !== undefined;\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById('mpl-warnings');\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent =\n", - " 'This browser does not support binary websocket messages. ' +\n", - " 'Performance may be slow.';\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = document.createElement('div');\n", - " this.root.setAttribute('style', 'display: inline-block');\n", - " this._root_extra_style(this.root);\n", - "\n", - " parent_element.appendChild(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message('supports_binary', { value: fig.supports_binary });\n", - " fig.send_message('send_image_mode', {});\n", - " if (fig.ratio !== 1) {\n", - " fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n", - " }\n", - " fig.send_message('refresh', {});\n", - " };\n", - "\n", - " this.imageObj.onload = function () {\n", - " if (fig.image_mode === 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function () {\n", - " fig.ws.close();\n", - " };\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "};\n", - "\n", - "mpl.figure.prototype._init_header = function () {\n", - " var titlebar = document.createElement('div');\n", - " titlebar.classList =\n", - " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", - " var titletext = document.createElement('div');\n", - " titletext.classList = 'ui-dialog-title';\n", - " titletext.setAttribute(\n", - " 'style',\n", - " 'width: 100%; text-align: center; padding: 3px;'\n", - " );\n", - " titlebar.appendChild(titletext);\n", - " this.root.appendChild(titlebar);\n", - " this.header = titletext;\n", - "};\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", - "\n", - "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", - "\n", - "mpl.figure.prototype._init_canvas = function () {\n", - " var fig = this;\n", - "\n", - " var canvas_div = (this.canvas_div = document.createElement('div'));\n", - " canvas_div.setAttribute(\n", - " 'style',\n", - " 'border: 1px solid #ddd;' +\n", - " 'box-sizing: content-box;' +\n", - " 'clear: both;' +\n", - " 'min-height: 1px;' +\n", - " 'min-width: 1px;' +\n", - " 'outline: 0;' +\n", - " 'overflow: hidden;' +\n", - " 'position: relative;' +\n", - " 'resize: both;'\n", - " );\n", - "\n", - " function on_keyboard_event_closure(name) {\n", - " return function (event) {\n", - " return fig.key_event(event, name);\n", - " };\n", - " }\n", - "\n", - " canvas_div.addEventListener(\n", - " 'keydown',\n", - " on_keyboard_event_closure('key_press')\n", - " );\n", - " canvas_div.addEventListener(\n", - " 'keyup',\n", - " on_keyboard_event_closure('key_release')\n", - " );\n", - "\n", - " this._canvas_extra_style(canvas_div);\n", - " this.root.appendChild(canvas_div);\n", - "\n", - " var canvas = (this.canvas = document.createElement('canvas'));\n", - " canvas.classList.add('mpl-canvas');\n", - " canvas.setAttribute('style', 'box-sizing: content-box;');\n", - "\n", - " this.context = canvas.getContext('2d');\n", - "\n", - " var backingStore =\n", - " this.context.backingStorePixelRatio ||\n", - " this.context.webkitBackingStorePixelRatio ||\n", - " this.context.mozBackingStorePixelRatio ||\n", - " this.context.msBackingStorePixelRatio ||\n", - " this.context.oBackingStorePixelRatio ||\n", - " this.context.backingStorePixelRatio ||\n", - " 1;\n", - "\n", - " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", - " 'canvas'\n", - " ));\n", - " rubberband_canvas.setAttribute(\n", - " 'style',\n", - " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", - " );\n", - "\n", - " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", - " if (this.ResizeObserver === undefined) {\n", - " if (window.ResizeObserver !== undefined) {\n", - " this.ResizeObserver = window.ResizeObserver;\n", - " } else {\n", - " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", - " this.ResizeObserver = obs.ResizeObserver;\n", - " }\n", - " }\n", - "\n", - " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", - " var nentries = entries.length;\n", - " for (var i = 0; i < nentries; i++) {\n", - " var entry = entries[i];\n", - " var width, height;\n", - " if (entry.contentBoxSize) {\n", - " if (entry.contentBoxSize instanceof Array) {\n", - " // Chrome 84 implements new version of spec.\n", - " width = entry.contentBoxSize[0].inlineSize;\n", - " height = entry.contentBoxSize[0].blockSize;\n", - " } else {\n", - " // Firefox implements old version of spec.\n", - " width = entry.contentBoxSize.inlineSize;\n", - " height = entry.contentBoxSize.blockSize;\n", - " }\n", - " } else {\n", - " // Chrome <84 implements even older version of spec.\n", - " width = entry.contentRect.width;\n", - " height = entry.contentRect.height;\n", - " }\n", - "\n", - " // Keep the size of the canvas and rubber band canvas in sync with\n", - " // the canvas container.\n", - " if (entry.devicePixelContentBoxSize) {\n", - " // Chrome 84 implements new version of spec.\n", - " canvas.setAttribute(\n", - " 'width',\n", - " entry.devicePixelContentBoxSize[0].inlineSize\n", - " );\n", - " canvas.setAttribute(\n", - " 'height',\n", - " entry.devicePixelContentBoxSize[0].blockSize\n", - " );\n", - " } else {\n", - " canvas.setAttribute('width', width * fig.ratio);\n", - " canvas.setAttribute('height', height * fig.ratio);\n", - " }\n", - " canvas.setAttribute(\n", - " 'style',\n", - " 'width: ' + width + 'px; height: ' + height + 'px;'\n", - " );\n", - "\n", - " rubberband_canvas.setAttribute('width', width);\n", - " rubberband_canvas.setAttribute('height', height);\n", - "\n", - " // And update the size in Python. We ignore the initial 0/0 size\n", - " // that occurs as the element is placed into the DOM, which should\n", - " // otherwise not happen due to the minimum size styling.\n", - " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", - " fig.request_resize(width, height);\n", - " }\n", - " }\n", - " });\n", - " this.resizeObserverInstance.observe(canvas_div);\n", - "\n", - " function on_mouse_event_closure(name) {\n", - " return function (event) {\n", - " return fig.mouse_event(event, name);\n", - " };\n", - " }\n", - "\n", - " rubberband_canvas.addEventListener(\n", - " 'mousedown',\n", - " on_mouse_event_closure('button_press')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseup',\n", - " on_mouse_event_closure('button_release')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'dblclick',\n", - " on_mouse_event_closure('dblclick')\n", - " );\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband_canvas.addEventListener(\n", - " 'mousemove',\n", - " on_mouse_event_closure('motion_notify')\n", - " );\n", - "\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseenter',\n", - " on_mouse_event_closure('figure_enter')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseleave',\n", - " on_mouse_event_closure('figure_leave')\n", - " );\n", - "\n", - " canvas_div.addEventListener('wheel', function (event) {\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " on_mouse_event_closure('scroll')(event);\n", - " });\n", - "\n", - " canvas_div.appendChild(canvas);\n", - " canvas_div.appendChild(rubberband_canvas);\n", - "\n", - " this.rubberband_context = rubberband_canvas.getContext('2d');\n", - " this.rubberband_context.strokeStyle = '#000000';\n", - "\n", - " this._resize_canvas = function (width, height, forward) {\n", - " if (forward) {\n", - " canvas_div.style.width = width + 'px';\n", - " canvas_div.style.height = height + 'px';\n", - " }\n", - " };\n", - "\n", - " // Disable right mouse context menu.\n", - " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", - " event.preventDefault();\n", - " return false;\n", - " });\n", - "\n", - " function set_focus() {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "};\n", - "\n", - "mpl.figure.prototype._init_toolbar = function () {\n", - " var fig = this;\n", - "\n", - " var toolbar = document.createElement('div');\n", - " toolbar.classList = 'mpl-toolbar';\n", - " this.root.appendChild(toolbar);\n", - "\n", - " function on_click_closure(name) {\n", - " return function (_event) {\n", - " return fig.toolbar_button_onclick(name);\n", - " };\n", - " }\n", - "\n", - " function on_mouseover_closure(tooltip) {\n", - " return function (event) {\n", - " if (!event.currentTarget.disabled) {\n", - " return fig.toolbar_button_onmouseover(tooltip);\n", - " }\n", - " };\n", - " }\n", - "\n", - " fig.buttons = {};\n", - " var buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'mpl-button-group';\n", - " for (var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " /* Instead of a spacer, we start a new button group. */\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - " buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'mpl-button-group';\n", - " continue;\n", - " }\n", - "\n", - " var button = (fig.buttons[name] = document.createElement('button'));\n", - " button.classList = 'mpl-widget';\n", - " button.setAttribute('role', 'button');\n", - " button.setAttribute('aria-disabled', 'false');\n", - " button.addEventListener('click', on_click_closure(method_name));\n", - " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", - "\n", - " var icon_img = document.createElement('img');\n", - " icon_img.src = '_images/' + image + '.png';\n", - " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", - " icon_img.alt = tooltip;\n", - " button.appendChild(icon_img);\n", - "\n", - " buttonGroup.appendChild(button);\n", - " }\n", - "\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - "\n", - " var fmt_picker = document.createElement('select');\n", - " fmt_picker.classList = 'mpl-widget';\n", - " toolbar.appendChild(fmt_picker);\n", - " this.format_dropdown = fmt_picker;\n", - "\n", - " for (var ind in mpl.extensions) {\n", - " var fmt = mpl.extensions[ind];\n", - " var option = document.createElement('option');\n", - " option.selected = fmt === mpl.default_extension;\n", - " option.innerHTML = fmt;\n", - " fmt_picker.appendChild(option);\n", - " }\n", - "\n", - " var status_bar = document.createElement('span');\n", - " status_bar.classList = 'mpl-message';\n", - " toolbar.appendChild(status_bar);\n", - " this.message = status_bar;\n", - "};\n", - "\n", - "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", - " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", - " // which will in turn request a refresh of the image.\n", - " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", - "};\n", - "\n", - "mpl.figure.prototype.send_message = function (type, properties) {\n", - " properties['type'] = type;\n", - " properties['figure_id'] = this.id;\n", - " this.ws.send(JSON.stringify(properties));\n", - "};\n", - "\n", - "mpl.figure.prototype.send_draw_message = function () {\n", - " if (!this.waiting) {\n", - " this.waiting = true;\n", - " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", - " var format_dropdown = fig.format_dropdown;\n", - " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", - " fig.ondownload(fig, format);\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", - " var size = msg['size'];\n", - " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", - " fig._resize_canvas(size[0], size[1], msg['forward']);\n", - " fig.send_message('refresh', {});\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", - " var x0 = msg['x0'] / fig.ratio;\n", - " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", - " var x1 = msg['x1'] / fig.ratio;\n", - " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", - " x0 = Math.floor(x0) + 0.5;\n", - " y0 = Math.floor(y0) + 0.5;\n", - " x1 = Math.floor(x1) + 0.5;\n", - " y1 = Math.floor(y1) + 0.5;\n", - " var min_x = Math.min(x0, x1);\n", - " var min_y = Math.min(y0, y1);\n", - " var width = Math.abs(x1 - x0);\n", - " var height = Math.abs(y1 - y0);\n", - "\n", - " fig.rubberband_context.clearRect(\n", - " 0,\n", - " 0,\n", - " fig.canvas.width / fig.ratio,\n", - " fig.canvas.height / fig.ratio\n", - " );\n", - "\n", - " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", - " // Updates the figure title.\n", - " fig.header.textContent = msg['label'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", - " var cursor = msg['cursor'];\n", - " switch (cursor) {\n", - " case 0:\n", - " cursor = 'pointer';\n", - " break;\n", - " case 1:\n", - " cursor = 'default';\n", - " break;\n", - " case 2:\n", - " cursor = 'crosshair';\n", - " break;\n", - " case 3:\n", - " cursor = 'move';\n", - " break;\n", - " }\n", - " fig.rubberband_canvas.style.cursor = cursor;\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_message = function (fig, msg) {\n", - " fig.message.textContent = msg['message'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", - " // Request the server to send over a new figure.\n", - " fig.send_draw_message();\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", - " fig.image_mode = msg['mode'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", - " for (var key in msg) {\n", - " if (!(key in fig.buttons)) {\n", - " continue;\n", - " }\n", - " fig.buttons[key].disabled = !msg[key];\n", - " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", - " if (msg['mode'] === 'PAN') {\n", - " fig.buttons['Pan'].classList.add('active');\n", - " fig.buttons['Zoom'].classList.remove('active');\n", - " } else if (msg['mode'] === 'ZOOM') {\n", - " fig.buttons['Pan'].classList.remove('active');\n", - " fig.buttons['Zoom'].classList.add('active');\n", - " } else {\n", - " fig.buttons['Pan'].classList.remove('active');\n", - " fig.buttons['Zoom'].classList.remove('active');\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.updated_canvas_event = function () {\n", - " // Called whenever the canvas gets updated.\n", - " this.send_message('ack', {});\n", - "};\n", - "\n", - "// A function to construct a web socket function for onmessage handling.\n", - "// Called in the figure constructor.\n", - "mpl.figure.prototype._make_on_message_function = function (fig) {\n", - " return function socket_on_message(evt) {\n", - " if (evt.data instanceof Blob) {\n", - " var img = evt.data;\n", - " if (img.type !== 'image/png') {\n", - " /* FIXME: We get \"Resource interpreted as Image but\n", - " * transferred with MIME type text/plain:\" errors on\n", - " * Chrome. But how to set the MIME type? It doesn't seem\n", - " * to be part of the websocket stream */\n", - " img.type = 'image/png';\n", - " }\n", - "\n", - " /* Free the memory for the previous frames */\n", - " if (fig.imageObj.src) {\n", - " (window.URL || window.webkitURL).revokeObjectURL(\n", - " fig.imageObj.src\n", - " );\n", - " }\n", - "\n", - " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", - " img\n", - " );\n", - " fig.updated_canvas_event();\n", - " fig.waiting = false;\n", - " return;\n", - " } else if (\n", - " typeof evt.data === 'string' &&\n", - " evt.data.slice(0, 21) === 'data:image/png;base64'\n", - " ) {\n", - " fig.imageObj.src = evt.data;\n", - " fig.updated_canvas_event();\n", - " fig.waiting = false;\n", - " return;\n", - " }\n", - "\n", - " var msg = JSON.parse(evt.data);\n", - " var msg_type = msg['type'];\n", - "\n", - " // Call the \"handle_{type}\" callback, which takes\n", - " // the figure and JSON message as its only arguments.\n", - " try {\n", - " var callback = fig['handle_' + msg_type];\n", - " } catch (e) {\n", - " console.log(\n", - " \"No handler for the '\" + msg_type + \"' message type: \",\n", - " msg\n", - " );\n", - " return;\n", - " }\n", - "\n", - " if (callback) {\n", - " try {\n", - " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", - " callback(fig, msg);\n", - " } catch (e) {\n", - " console.log(\n", - " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", - " e,\n", - " e.stack,\n", - " msg\n", - " );\n", - " }\n", - " }\n", - " };\n", - "};\n", - "\n", - "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", - "mpl.findpos = function (e) {\n", - " //this section is from http://www.quirksmode.org/js/events_properties.html\n", - " var targ;\n", - " if (!e) {\n", - " e = window.event;\n", - " }\n", - " if (e.target) {\n", - " targ = e.target;\n", - " } else if (e.srcElement) {\n", - " targ = e.srcElement;\n", - " }\n", - " if (targ.nodeType === 3) {\n", - " // defeat Safari bug\n", - " targ = targ.parentNode;\n", - " }\n", - "\n", - " // pageX,Y are the mouse positions relative to the document\n", - " var boundingRect = targ.getBoundingClientRect();\n", - " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", - " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", - "\n", - " return { x: x, y: y };\n", - "};\n", - "\n", - "/*\n", - " * return a copy of an object with only non-object keys\n", - " * we need this to avoid circular references\n", - " * http://stackoverflow.com/a/24161582/3208463\n", - " */\n", - "function simpleKeys(original) {\n", - " return Object.keys(original).reduce(function (obj, key) {\n", - " if (typeof original[key] !== 'object') {\n", - " obj[key] = original[key];\n", - " }\n", - " return obj;\n", - " }, {});\n", - "}\n", - "\n", - "mpl.figure.prototype.mouse_event = function (event, name) {\n", - " var canvas_pos = mpl.findpos(event);\n", - "\n", - " if (name === 'button_press') {\n", - " this.canvas.focus();\n", - " this.canvas_div.focus();\n", - " }\n", - "\n", - " var x = canvas_pos.x * this.ratio;\n", - " var y = canvas_pos.y * this.ratio;\n", - "\n", - " this.send_message(name, {\n", - " x: x,\n", - " y: y,\n", - " button: event.button,\n", - " step: event.step,\n", - " guiEvent: simpleKeys(event),\n", - " });\n", - "\n", - " /* This prevents the web browser from automatically changing to\n", - " * the text insertion cursor when the button is pressed. We want\n", - " * to control all of the cursor setting manually through the\n", - " * 'cursor' event from matplotlib */\n", - " event.preventDefault();\n", - " return false;\n", - "};\n", - "\n", - "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", - " // Handle any extra behaviour associated with a key event\n", - "};\n", - "\n", - "mpl.figure.prototype.key_event = function (event, name) {\n", - " // Prevent repeat events\n", - " if (name === 'key_press') {\n", - " if (event.key === this._key) {\n", - " return;\n", - " } else {\n", - " this._key = event.key;\n", - " }\n", - " }\n", - " if (name === 'key_release') {\n", - " this._key = null;\n", - " }\n", - "\n", - " var value = '';\n", - " if (event.ctrlKey && event.key !== 'Control') {\n", - " value += 'ctrl+';\n", - " }\n", - " else if (event.altKey && event.key !== 'Alt') {\n", - " value += 'alt+';\n", - " }\n", - " else if (event.shiftKey && event.key !== 'Shift') {\n", - " value += 'shift+';\n", - " }\n", - "\n", - " value += 'k' + event.key;\n", - "\n", - " this._key_event_extra(event, name);\n", - "\n", - " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", - " return false;\n", - "};\n", - "\n", - "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", - " if (name === 'download') {\n", - " this.handle_save(this, null);\n", - " } else {\n", - " this.send_message('toolbar_button', { name: name });\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", - " this.message.textContent = tooltip;\n", - "};\n", - "\n", - "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", - "// prettier-ignore\n", - "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", - "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", - "\n", - "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", - "\n", - "mpl.default_extension = \"png\";/* global mpl */\n", - "\n", - "var comm_websocket_adapter = function (comm) {\n", - " // Create a \"websocket\"-like object which calls the given IPython comm\n", - " // object with the appropriate methods. Currently this is a non binary\n", - " // socket, so there is still some room for performance tuning.\n", - " var ws = {};\n", - "\n", - " ws.binaryType = comm.kernel.ws.binaryType;\n", - " ws.readyState = comm.kernel.ws.readyState;\n", - " function updateReadyState(_event) {\n", - " if (comm.kernel.ws) {\n", - " ws.readyState = comm.kernel.ws.readyState;\n", - " } else {\n", - " ws.readyState = 3; // Closed state.\n", - " }\n", - " }\n", - " comm.kernel.ws.addEventListener('open', updateReadyState);\n", - " comm.kernel.ws.addEventListener('close', updateReadyState);\n", - " comm.kernel.ws.addEventListener('error', updateReadyState);\n", - "\n", - " ws.close = function () {\n", - " comm.close();\n", - " };\n", - " ws.send = function (m) {\n", - " //console.log('sending', m);\n", - " comm.send(m);\n", - " };\n", - " // Register the callback with on_msg.\n", - " comm.on_msg(function (msg) {\n", - " //console.log('receiving', msg['content']['data'], msg);\n", - " var data = msg['content']['data'];\n", - " if (data['blob'] !== undefined) {\n", - " data = {\n", - " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", - " };\n", - " }\n", - " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", - " ws.onmessage(data);\n", - " });\n", - " return ws;\n", - "};\n", - "\n", - "mpl.mpl_figure_comm = function (comm, msg) {\n", - " // This is the function which gets called when the mpl process\n", - " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", - "\n", - " var id = msg.content.data.id;\n", - " // Get hold of the div created by the display call when the Comm\n", - " // socket was opened in Python.\n", - " var element = document.getElementById(id);\n", - " var ws_proxy = comm_websocket_adapter(comm);\n", - "\n", - " function ondownload(figure, _format) {\n", - " window.open(figure.canvas.toDataURL());\n", - " }\n", - "\n", - " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", - "\n", - " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", - " // web socket which is closed, not our websocket->open comm proxy.\n", - " ws_proxy.onopen();\n", - "\n", - " fig.parent_element = element;\n", - " fig.cell_info = mpl.find_output_cell(\"
\");\n", - " if (!fig.cell_info) {\n", - " console.error('Failed to find cell for figure', id, fig);\n", - " return;\n", - " }\n", - " fig.cell_info[0].output_area.element.on(\n", - " 'cleared',\n", - " { fig: fig },\n", - " fig._remove_fig_handler\n", - " );\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_close = function (fig, msg) {\n", - " var width = fig.canvas.width / fig.ratio;\n", - " fig.cell_info[0].output_area.element.off(\n", - " 'cleared',\n", - " fig._remove_fig_handler\n", - " );\n", - " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", - "\n", - " // Update the output cell to use the data from the current canvas.\n", - " fig.push_to_output();\n", - " var dataURL = fig.canvas.toDataURL();\n", - " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", - " // the notebook keyboard shortcuts fail.\n", - " IPython.keyboard_manager.enable();\n", - " fig.parent_element.innerHTML =\n", - " '';\n", - " fig.close_ws(fig, msg);\n", - "};\n", - "\n", - "mpl.figure.prototype.close_ws = function (fig, msg) {\n", - " fig.send_message('closing', msg);\n", - " // fig.ws.close()\n", - "};\n", - "\n", - "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", - " // Turn the data on the canvas into data in the output cell.\n", - " var width = this.canvas.width / this.ratio;\n", - " var dataURL = this.canvas.toDataURL();\n", - " this.cell_info[1]['text/html'] =\n", - " '';\n", - "};\n", - "\n", - "mpl.figure.prototype.updated_canvas_event = function () {\n", - " // Tell IPython that the notebook contents must change.\n", - " IPython.notebook.set_dirty(true);\n", - " this.send_message('ack', {});\n", - " var fig = this;\n", - " // Wait a second, then push the new image to the DOM so\n", - " // that it is saved nicely (might be nice to debounce this).\n", - " setTimeout(function () {\n", - " fig.push_to_output();\n", - " }, 1000);\n", - "};\n", - "\n", - "mpl.figure.prototype._init_toolbar = function () {\n", - " var fig = this;\n", - "\n", - " var toolbar = document.createElement('div');\n", - " toolbar.classList = 'btn-toolbar';\n", - " this.root.appendChild(toolbar);\n", - "\n", - " function on_click_closure(name) {\n", - " return function (_event) {\n", - " return fig.toolbar_button_onclick(name);\n", - " };\n", - " }\n", - "\n", - " function on_mouseover_closure(tooltip) {\n", - " return function (event) {\n", - " if (!event.currentTarget.disabled) {\n", - " return fig.toolbar_button_onmouseover(tooltip);\n", - " }\n", - " };\n", - " }\n", - "\n", - " fig.buttons = {};\n", - " var buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'btn-group';\n", - " var button;\n", - " for (var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " /* Instead of a spacer, we start a new button group. */\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - " buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'btn-group';\n", - " continue;\n", - " }\n", - "\n", - " button = fig.buttons[name] = document.createElement('button');\n", - " button.classList = 'btn btn-default';\n", - " button.href = '#';\n", - " button.title = name;\n", - " button.innerHTML = '';\n", - " button.addEventListener('click', on_click_closure(method_name));\n", - " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", - " buttonGroup.appendChild(button);\n", - " }\n", - "\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - "\n", - " // Add the status bar.\n", - " var status_bar = document.createElement('span');\n", - " status_bar.classList = 'mpl-message pull-right';\n", - " toolbar.appendChild(status_bar);\n", - " this.message = status_bar;\n", - "\n", - " // Add the close button to the window.\n", - " var buttongrp = document.createElement('div');\n", - " buttongrp.classList = 'btn-group inline pull-right';\n", - " button = document.createElement('button');\n", - " button.classList = 'btn btn-mini btn-primary';\n", - " button.href = '#';\n", - " button.title = 'Stop Interaction';\n", - " button.innerHTML = '';\n", - " button.addEventListener('click', function (_evt) {\n", - " fig.handle_close(fig, {});\n", - " });\n", - " button.addEventListener(\n", - " 'mouseover',\n", - " on_mouseover_closure('Stop Interaction')\n", - " );\n", - " buttongrp.appendChild(button);\n", - " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", - " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", - "};\n", - "\n", - "mpl.figure.prototype._remove_fig_handler = function (event) {\n", - " var fig = event.data.fig;\n", - " if (event.target !== this) {\n", - " // Ignore bubbled events from children.\n", - " return;\n", - " }\n", - " fig.close_ws(fig, {});\n", - "};\n", - "\n", - "mpl.figure.prototype._root_extra_style = function (el) {\n", - " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", - "};\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function (el) {\n", - " // this is important to make the div 'focusable\n", - " el.setAttribute('tabindex', 0);\n", - " // reach out to IPython and tell the keyboard manager to turn it's self\n", - " // off when our div gets focus\n", - "\n", - " // location in version 3\n", - " if (IPython.notebook.keyboard_manager) {\n", - " IPython.notebook.keyboard_manager.register_events(el);\n", - " } else {\n", - " // location in version 2\n", - " IPython.keyboard_manager.register_events(el);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", - " var manager = IPython.notebook.keyboard_manager;\n", - " if (!manager) {\n", - " manager = IPython.keyboard_manager;\n", - " }\n", - "\n", - " // Check for shift+enter\n", - " if (event.shiftKey && event.which === 13) {\n", - " this.canvas_div.blur();\n", - " // select the cell after this one\n", - " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", - " IPython.notebook.select(index + 1);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", - " fig.ondownload(fig, null);\n", - "};\n", - "\n", - "mpl.find_output_cell = function (html_output) {\n", - " // Return the cell and output element which can be found *uniquely* in the notebook.\n", - " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", - " // IPython event is triggered only after the cells have been serialised, which for\n", - " // our purposes (turning an active figure into a static one), is too late.\n", - " var cells = IPython.notebook.get_cells();\n", - " var ncells = cells.length;\n", - " for (var i = 0; i < ncells; i++) {\n", - " var cell = cells[i];\n", - " if (cell.cell_type === 'code') {\n", - " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", - " var data = cell.output_area.outputs[j];\n", - " if (data.data) {\n", - " // IPython >= 3 moved mimebundle to data attribute of output\n", - " data = data.data;\n", - " }\n", - " if (data['text/html'] === html_output) {\n", - " return [cell, data, j];\n", - " }\n", - " }\n", - " }\n", - " }\n", - "};\n", - "\n", - "// Register the function which deals with the matplotlib target/channel.\n", - "// The kernel may be null if the page has been refreshed.\n", - "if (IPython.notebook.kernel !== null) {\n", - " IPython.notebook.kernel.comm_manager.register_target(\n", - " 'matplotlib',\n", - " mpl.mpl_figure_comm\n", - " );\n", - "}\n" - ], + "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.get_websocket_type = function () {\n if (typeof WebSocket !== 'undefined') {\n return WebSocket;\n } else if (typeof MozWebSocket !== 'undefined') {\n return MozWebSocket;\n } else {\n alert(\n 'Your browser does not have WebSocket support. ' +\n 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n 'Firefox 4 and 5 are also supported but you ' +\n 'have to enable WebSockets in about:config.'\n );\n }\n};\n\nmpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n this.id = figure_id;\n\n this.ws = websocket;\n\n this.supports_binary = this.ws.binaryType !== undefined;\n\n if (!this.supports_binary) {\n var warnings = document.getElementById('mpl-warnings');\n if (warnings) {\n warnings.style.display = 'block';\n warnings.textContent =\n 'This browser does not support binary websocket messages. ' +\n 'Performance may be slow.';\n }\n }\n\n this.imageObj = new Image();\n\n this.context = undefined;\n this.message = undefined;\n this.canvas = undefined;\n this.rubberband_canvas = undefined;\n this.rubberband_context = undefined;\n this.format_dropdown = undefined;\n\n this.image_mode = 'full';\n\n this.root = document.createElement('div');\n this.root.setAttribute('style', 'display: inline-block');\n this._root_extra_style(this.root);\n\n parent_element.appendChild(this.root);\n\n this._init_header(this);\n this._init_canvas(this);\n this._init_toolbar(this);\n\n var fig = this;\n\n this.waiting = false;\n\n this.ws.onopen = function () {\n fig.send_message('supports_binary', { value: fig.supports_binary });\n fig.send_message('send_image_mode', {});\n if (fig.ratio !== 1) {\n fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n }\n fig.send_message('refresh', {});\n };\n\n this.imageObj.onload = function () {\n if (fig.image_mode === 'full') {\n // Full images could contain transparency (where diff images\n // almost always do), so we need to clear the canvas so that\n // there is no ghosting.\n fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n }\n fig.context.drawImage(fig.imageObj, 0, 0);\n };\n\n this.imageObj.onunload = function () {\n fig.ws.close();\n };\n\n this.ws.onmessage = this._make_on_message_function(this);\n\n this.ondownload = ondownload;\n};\n\nmpl.figure.prototype._init_header = function () {\n var titlebar = document.createElement('div');\n titlebar.classList =\n 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n var titletext = document.createElement('div');\n titletext.classList = 'ui-dialog-title';\n titletext.setAttribute(\n 'style',\n 'width: 100%; text-align: center; padding: 3px;'\n );\n titlebar.appendChild(titletext);\n this.root.appendChild(titlebar);\n this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n var fig = this;\n\n var canvas_div = (this.canvas_div = document.createElement('div'));\n canvas_div.setAttribute(\n 'style',\n 'border: 1px solid #ddd;' +\n 'box-sizing: content-box;' +\n 'clear: both;' +\n 'min-height: 1px;' +\n 'min-width: 1px;' +\n 'outline: 0;' +\n 'overflow: hidden;' +\n 'position: relative;' +\n 'resize: both;'\n );\n\n function on_keyboard_event_closure(name) {\n return function (event) {\n return fig.key_event(event, name);\n };\n }\n\n canvas_div.addEventListener(\n 'keydown',\n on_keyboard_event_closure('key_press')\n );\n canvas_div.addEventListener(\n 'keyup',\n on_keyboard_event_closure('key_release')\n );\n\n this._canvas_extra_style(canvas_div);\n this.root.appendChild(canvas_div);\n\n var canvas = (this.canvas = document.createElement('canvas'));\n canvas.classList.add('mpl-canvas');\n canvas.setAttribute('style', 'box-sizing: content-box;');\n\n this.context = canvas.getContext('2d');\n\n var backingStore =\n this.context.backingStorePixelRatio ||\n this.context.webkitBackingStorePixelRatio ||\n this.context.mozBackingStorePixelRatio ||\n this.context.msBackingStorePixelRatio ||\n this.context.oBackingStorePixelRatio ||\n this.context.backingStorePixelRatio ||\n 1;\n\n this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n 'canvas'\n ));\n rubberband_canvas.setAttribute(\n 'style',\n 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n );\n\n // Apply a ponyfill if ResizeObserver is not implemented by browser.\n if (this.ResizeObserver === undefined) {\n if (window.ResizeObserver !== undefined) {\n this.ResizeObserver = window.ResizeObserver;\n } else {\n var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n this.ResizeObserver = obs.ResizeObserver;\n }\n }\n\n this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n var nentries = entries.length;\n for (var i = 0; i < nentries; i++) {\n var entry = entries[i];\n var width, height;\n if (entry.contentBoxSize) {\n if (entry.contentBoxSize instanceof Array) {\n // Chrome 84 implements new version of spec.\n width = entry.contentBoxSize[0].inlineSize;\n height = entry.contentBoxSize[0].blockSize;\n } else {\n // Firefox implements old version of spec.\n width = entry.contentBoxSize.inlineSize;\n height = entry.contentBoxSize.blockSize;\n }\n } else {\n // Chrome <84 implements even older version of spec.\n width = entry.contentRect.width;\n height = entry.contentRect.height;\n }\n\n // Keep the size of the canvas and rubber band canvas in sync with\n // the canvas container.\n if (entry.devicePixelContentBoxSize) {\n // Chrome 84 implements new version of spec.\n canvas.setAttribute(\n 'width',\n entry.devicePixelContentBoxSize[0].inlineSize\n );\n canvas.setAttribute(\n 'height',\n entry.devicePixelContentBoxSize[0].blockSize\n );\n } else {\n canvas.setAttribute('width', width * fig.ratio);\n canvas.setAttribute('height', height * fig.ratio);\n }\n canvas.setAttribute(\n 'style',\n 'width: ' + width + 'px; height: ' + height + 'px;'\n );\n\n rubberband_canvas.setAttribute('width', width);\n rubberband_canvas.setAttribute('height', height);\n\n // And update the size in Python. We ignore the initial 0/0 size\n // that occurs as the element is placed into the DOM, which should\n // otherwise not happen due to the minimum size styling.\n if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n fig.request_resize(width, height);\n }\n }\n });\n this.resizeObserverInstance.observe(canvas_div);\n\n function on_mouse_event_closure(name) {\n return function (event) {\n return fig.mouse_event(event, name);\n };\n }\n\n rubberband_canvas.addEventListener(\n 'mousedown',\n on_mouse_event_closure('button_press')\n );\n rubberband_canvas.addEventListener(\n 'mouseup',\n on_mouse_event_closure('button_release')\n );\n rubberband_canvas.addEventListener(\n 'dblclick',\n on_mouse_event_closure('dblclick')\n );\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband_canvas.addEventListener(\n 'mousemove',\n on_mouse_event_closure('motion_notify')\n );\n\n rubberband_canvas.addEventListener(\n 'mouseenter',\n on_mouse_event_closure('figure_enter')\n );\n rubberband_canvas.addEventListener(\n 'mouseleave',\n on_mouse_event_closure('figure_leave')\n );\n\n canvas_div.addEventListener('wheel', function (event) {\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n on_mouse_event_closure('scroll')(event);\n });\n\n canvas_div.appendChild(canvas);\n canvas_div.appendChild(rubberband_canvas);\n\n this.rubberband_context = rubberband_canvas.getContext('2d');\n this.rubberband_context.strokeStyle = '#000000';\n\n this._resize_canvas = function (width, height, forward) {\n if (forward) {\n canvas_div.style.width = width + 'px';\n canvas_div.style.height = height + 'px';\n }\n };\n\n // Disable right mouse context menu.\n this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n event.preventDefault();\n return false;\n });\n\n function set_focus() {\n canvas.focus();\n canvas_div.focus();\n }\n\n window.setTimeout(set_focus, 100);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'mpl-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n for (var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n continue;\n }\n\n var button = (fig.buttons[name] = document.createElement('button'));\n button.classList = 'mpl-widget';\n button.setAttribute('role', 'button');\n button.setAttribute('aria-disabled', 'false');\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n var icon_img = document.createElement('img');\n icon_img.src = '_images/' + image + '.png';\n icon_img.srcset = '_images/' + image + '_large.png 2x';\n icon_img.alt = tooltip;\n button.appendChild(icon_img);\n\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n var fmt_picker = document.createElement('select');\n fmt_picker.classList = 'mpl-widget';\n toolbar.appendChild(fmt_picker);\n this.format_dropdown = fmt_picker;\n\n for (var ind in mpl.extensions) {\n var fmt = mpl.extensions[ind];\n var option = document.createElement('option');\n option.selected = fmt === mpl.default_extension;\n option.innerHTML = fmt;\n fmt_picker.appendChild(option);\n }\n\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n};\n\nmpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n // which will in turn request a refresh of the image.\n this.send_message('resize', { width: x_pixels, height: y_pixels });\n};\n\nmpl.figure.prototype.send_message = function (type, properties) {\n properties['type'] = type;\n properties['figure_id'] = this.id;\n this.ws.send(JSON.stringify(properties));\n};\n\nmpl.figure.prototype.send_draw_message = function () {\n if (!this.waiting) {\n this.waiting = true;\n this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n var format_dropdown = fig.format_dropdown;\n var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n fig.ondownload(fig, format);\n};\n\nmpl.figure.prototype.handle_resize = function (fig, msg) {\n var size = msg['size'];\n if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n fig._resize_canvas(size[0], size[1], msg['forward']);\n fig.send_message('refresh', {});\n }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n var x0 = msg['x0'] / fig.ratio;\n var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n var x1 = msg['x1'] / fig.ratio;\n var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n x0 = Math.floor(x0) + 0.5;\n y0 = Math.floor(y0) + 0.5;\n x1 = Math.floor(x1) + 0.5;\n y1 = Math.floor(y1) + 0.5;\n var min_x = Math.min(x0, x1);\n var min_y = Math.min(y0, y1);\n var width = Math.abs(x1 - x0);\n var height = Math.abs(y1 - y0);\n\n fig.rubberband_context.clearRect(\n 0,\n 0,\n fig.canvas.width / fig.ratio,\n fig.canvas.height / fig.ratio\n );\n\n fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n var cursor = msg['cursor'];\n switch (cursor) {\n case 0:\n cursor = 'pointer';\n break;\n case 1:\n cursor = 'default';\n break;\n case 2:\n cursor = 'crosshair';\n break;\n case 3:\n cursor = 'move';\n break;\n }\n fig.rubberband_canvas.style.cursor = cursor;\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n for (var key in msg) {\n if (!(key in fig.buttons)) {\n continue;\n }\n fig.buttons[key].disabled = !msg[key];\n fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n if (msg['mode'] === 'PAN') {\n fig.buttons['Pan'].classList.add('active');\n fig.buttons['Zoom'].classList.remove('active');\n } else if (msg['mode'] === 'ZOOM') {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.add('active');\n } else {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.remove('active');\n }\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n // Called whenever the canvas gets updated.\n this.send_message('ack', {});\n};\n\n// A function to construct a web socket function for onmessage handling.\n// Called in the figure constructor.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n return function socket_on_message(evt) {\n if (evt.data instanceof Blob) {\n var img = evt.data;\n if (img.type !== 'image/png') {\n /* FIXME: We get \"Resource interpreted as Image but\n * transferred with MIME type text/plain:\" errors on\n * Chrome. But how to set the MIME type? It doesn't seem\n * to be part of the websocket stream */\n img.type = 'image/png';\n }\n\n /* Free the memory for the previous frames */\n if (fig.imageObj.src) {\n (window.URL || window.webkitURL).revokeObjectURL(\n fig.imageObj.src\n );\n }\n\n fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n img\n );\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n } else if (\n typeof evt.data === 'string' &&\n evt.data.slice(0, 21) === 'data:image/png;base64'\n ) {\n fig.imageObj.src = evt.data;\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n }\n\n var msg = JSON.parse(evt.data);\n var msg_type = msg['type'];\n\n // Call the \"handle_{type}\" callback, which takes\n // the figure and JSON message as its only arguments.\n try {\n var callback = fig['handle_' + msg_type];\n } catch (e) {\n console.log(\n \"No handler for the '\" + msg_type + \"' message type: \",\n msg\n );\n return;\n }\n\n if (callback) {\n try {\n // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n callback(fig, msg);\n } catch (e) {\n console.log(\n \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n e,\n e.stack,\n msg\n );\n }\n }\n };\n};\n\n// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.findpos = function (e) {\n //this section is from http://www.quirksmode.org/js/events_properties.html\n var targ;\n if (!e) {\n e = window.event;\n }\n if (e.target) {\n targ = e.target;\n } else if (e.srcElement) {\n targ = e.srcElement;\n }\n if (targ.nodeType === 3) {\n // defeat Safari bug\n targ = targ.parentNode;\n }\n\n // pageX,Y are the mouse positions relative to the document\n var boundingRect = targ.getBoundingClientRect();\n var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n\n return { x: x, y: y };\n};\n\n/*\n * return a copy of an object with only non-object keys\n * we need this to avoid circular references\n * http://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n return Object.keys(original).reduce(function (obj, key) {\n if (typeof original[key] !== 'object') {\n obj[key] = original[key];\n }\n return obj;\n }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n var canvas_pos = mpl.findpos(event);\n\n if (name === 'button_press') {\n this.canvas.focus();\n this.canvas_div.focus();\n }\n\n var x = canvas_pos.x * this.ratio;\n var y = canvas_pos.y * this.ratio;\n\n this.send_message(name, {\n x: x,\n y: y,\n button: event.button,\n step: event.step,\n guiEvent: simpleKeys(event),\n });\n\n /* This prevents the web browser from automatically changing to\n * the text insertion cursor when the button is pressed. We want\n * to control all of the cursor setting manually through the\n * 'cursor' event from matplotlib */\n event.preventDefault();\n return false;\n};\n\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n // Prevent repeat events\n if (name === 'key_press') {\n if (event.key === this._key) {\n return;\n } else {\n this._key = event.key;\n }\n }\n if (name === 'key_release') {\n this._key = null;\n }\n\n var value = '';\n if (event.ctrlKey && event.key !== 'Control') {\n value += 'ctrl+';\n }\n else if (event.altKey && event.key !== 'Alt') {\n value += 'alt+';\n }\n else if (event.shiftKey && event.key !== 'Shift') {\n value += 'shift+';\n }\n\n value += 'k' + event.key;\n\n this._key_event_extra(event, name);\n\n this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n return false;\n};\n\nmpl.figure.prototype.toolbar_button_onclick = function (name) {\n if (name === 'download') {\n this.handle_save(this, null);\n } else {\n this.send_message('toolbar_button', { name: name });\n }\n};\n\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar comm_websocket_adapter = function (comm) {\n // Create a \"websocket\"-like object which calls the given IPython comm\n // object with the appropriate methods. Currently this is a non binary\n // socket, so there is still some room for performance tuning.\n var ws = {};\n\n ws.binaryType = comm.kernel.ws.binaryType;\n ws.readyState = comm.kernel.ws.readyState;\n function updateReadyState(_event) {\n if (comm.kernel.ws) {\n ws.readyState = comm.kernel.ws.readyState;\n } else {\n ws.readyState = 3; // Closed state.\n }\n }\n comm.kernel.ws.addEventListener('open', updateReadyState);\n comm.kernel.ws.addEventListener('close', updateReadyState);\n comm.kernel.ws.addEventListener('error', updateReadyState);\n\n ws.close = function () {\n comm.close();\n };\n ws.send = function (m) {\n //console.log('sending', m);\n comm.send(m);\n };\n // Register the callback with on_msg.\n comm.on_msg(function (msg) {\n //console.log('receiving', msg['content']['data'], msg);\n var data = msg['content']['data'];\n if (data['blob'] !== undefined) {\n data = {\n data: new Blob(msg['buffers'], { type: data['blob'] }),\n };\n }\n // Pass the mpl event to the overridden (by mpl) onmessage function.\n ws.onmessage(data);\n });\n return ws;\n};\n\nmpl.mpl_figure_comm = function (comm, msg) {\n // This is the function which gets called when the mpl process\n // starts-up an IPython Comm through the \"matplotlib\" channel.\n\n var id = msg.content.data.id;\n // Get hold of the div created by the display call when the Comm\n // socket was opened in Python.\n var element = document.getElementById(id);\n var ws_proxy = comm_websocket_adapter(comm);\n\n function ondownload(figure, _format) {\n window.open(figure.canvas.toDataURL());\n }\n\n var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n\n // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n // web socket which is closed, not our websocket->open comm proxy.\n ws_proxy.onopen();\n\n fig.parent_element = element;\n fig.cell_info = mpl.find_output_cell(\"
\");\n if (!fig.cell_info) {\n console.error('Failed to find cell for figure', id, fig);\n return;\n }\n fig.cell_info[0].output_area.element.on(\n 'cleared',\n { fig: fig },\n fig._remove_fig_handler\n );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n var width = fig.canvas.width / fig.ratio;\n fig.cell_info[0].output_area.element.off(\n 'cleared',\n fig._remove_fig_handler\n );\n fig.resizeObserverInstance.unobserve(fig.canvas_div);\n\n // Update the output cell to use the data from the current canvas.\n fig.push_to_output();\n var dataURL = fig.canvas.toDataURL();\n // Re-enable the keyboard manager in IPython - without this line, in FF,\n // the notebook keyboard shortcuts fail.\n IPython.keyboard_manager.enable();\n fig.parent_element.innerHTML =\n '';\n fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n fig.send_message('closing', msg);\n // fig.ws.close()\n};\n\nmpl.figure.prototype.push_to_output = function (_remove_interactive) {\n // Turn the data on the canvas into data in the output cell.\n var width = this.canvas.width / this.ratio;\n var dataURL = this.canvas.toDataURL();\n this.cell_info[1]['text/html'] =\n '';\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n // Tell IPython that the notebook contents must change.\n IPython.notebook.set_dirty(true);\n this.send_message('ack', {});\n var fig = this;\n // Wait a second, then push the new image to the DOM so\n // that it is saved nicely (might be nice to debounce this).\n setTimeout(function () {\n fig.push_to_output();\n }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'btn-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n var button;\n for (var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n continue;\n }\n\n button = fig.buttons[name] = document.createElement('button');\n button.classList = 'btn btn-default';\n button.href = '#';\n button.title = name;\n button.innerHTML = '';\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n // Add the status bar.\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message pull-right';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n\n // Add the close button to the window.\n var buttongrp = document.createElement('div');\n buttongrp.classList = 'btn-group inline pull-right';\n button = document.createElement('button');\n button.classList = 'btn btn-mini btn-primary';\n button.href = '#';\n button.title = 'Stop Interaction';\n button.innerHTML = '';\n button.addEventListener('click', function (_evt) {\n fig.handle_close(fig, {});\n });\n button.addEventListener(\n 'mouseover',\n on_mouseover_closure('Stop Interaction')\n );\n buttongrp.appendChild(button);\n var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n var fig = event.data.fig;\n if (event.target !== this) {\n // Ignore bubbled events from children.\n return;\n }\n fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n // this is important to make the div 'focusable\n el.setAttribute('tabindex', 0);\n // reach out to IPython and tell the keyboard manager to turn it's self\n // off when our div gets focus\n\n // location in version 3\n if (IPython.notebook.keyboard_manager) {\n IPython.notebook.keyboard_manager.register_events(el);\n } else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n var manager = IPython.notebook.keyboard_manager;\n if (!manager) {\n manager = IPython.keyboard_manager;\n }\n\n // Check for shift+enter\n if (event.shiftKey && event.which === 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n fig.ondownload(fig, null);\n};\n\nmpl.find_output_cell = function (html_output) {\n // Return the cell and output element which can be found *uniquely* in the notebook.\n // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n // IPython event is triggered only after the cells have been serialised, which for\n // our purposes (turning an active figure into a static one), is too late.\n var cells = IPython.notebook.get_cells();\n var ncells = cells.length;\n for (var i = 0; i < ncells; i++) {\n var cell = cells[i];\n if (cell.cell_type === 'code') {\n for (var j = 0; j < cell.output_area.outputs.length; j++) {\n var data = cell.output_area.outputs[j];\n if (data.data) {\n // IPython >= 3 moved mimebundle to data attribute of output\n data = data.data;\n }\n if (data['text/html'] === html_output) {\n return [cell, data, j];\n }\n }\n }\n }\n};\n\n// Register the function which deals with the matplotlib target/channel.\n// The kernel may be null if the page has been refreshed.\nif (IPython.notebook.kernel !== null) {\n IPython.notebook.kernel.comm_manager.register_target(\n 'matplotlib',\n mpl.mpl_figure_comm\n );\n}\n", "text/plain": [ "" ] @@ -1473,8 +511,7 @@ } ], "source": [ - "%matplotlib notebook\n", - "motor_dashboard.reset_figures()\n", + "\n", "history = dqn.fit(\n", " env,\n", " nb_steps=100000,\n", @@ -1552,6 +589,15 @@ "### 5.2 Testing" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "motor_dashboard.initialize()" + ] + }, { "cell_type": "code", "execution_count": 13, diff --git a/examples/reinforcement_learning_controllers/stable_baselines3_dqn_disc_pmsm_example.ipynb b/examples/reinforcement_learning_controllers/stable_baselines3_dqn_disc_pmsm_example.ipynb index 0813595b..d37c9dfc 100644 --- a/examples/reinforcement_learning_controllers/stable_baselines3_dqn_disc_pmsm_example.ipynb +++ b/examples/reinforcement_learning_controllers/stable_baselines3_dqn_disc_pmsm_example.ipynb @@ -111,6 +111,7 @@ ], "source": [ "%matplotlib notebook\n", + "# Use %matplotlib widget in Visual Studio Code\n", "import numpy as np\n", "from pathlib import Path\n", "import gym_electric_motor as gem\n", @@ -255,12 +256,12 @@ " reward_power=1\n", ")\n", "reward_logger = RewardLogger()\n", - "\n", + "motor_dashboard = MotorDashboard(state_plots=['i_sq', 'i_sd'], reward_plot=True)\n", "# creating gem environment\n", "env = gem.make( # define a PMSM with discrete action space\n", " \"Finite-CC-PMSM-v0\",\n", " # visualize the results\n", - " visualization=MotorDashboard(state_plots=['i_sq', 'i_sd'], reward_plot=True),\n", + " visualization=motor_dashboard,\n", " \n", " # parameterize the PMSM and update limitations\n", " motor=dict(\n", @@ -411,6 +412,15 @@ "Once you've setup the environment and defined your parameters starting the training is nothing more than a one-liner. For each algorithm all you have to do is call its ```learn()``` function. However, you should note that the execution of the training can take a long time. Currently, Stable-Baselines3 does not provide any means of saving the training reward for later visualization. Therefore, a ```RewardLogger``` callback is used for this environment (see code a few cells above)." ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "motor_dashboard.initialize()" + ] + }, { "cell_type": "code", "execution_count": 46, @@ -903,980 +913,7 @@ "outputs": [ { "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "/* global mpl */\n", - "window.mpl = {};\n", - "\n", - "mpl.get_websocket_type = function () {\n", - " if (typeof WebSocket !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof MozWebSocket !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert(\n", - " 'Your browser does not have WebSocket support. ' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.'\n", - " );\n", - " }\n", - "};\n", - "\n", - "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = this.ws.binaryType !== undefined;\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById('mpl-warnings');\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent =\n", - " 'This browser does not support binary websocket messages. ' +\n", - " 'Performance may be slow.';\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = document.createElement('div');\n", - " this.root.setAttribute('style', 'display: inline-block');\n", - " this._root_extra_style(this.root);\n", - "\n", - " parent_element.appendChild(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message('supports_binary', { value: fig.supports_binary });\n", - " fig.send_message('send_image_mode', {});\n", - " if (fig.ratio !== 1) {\n", - " fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n", - " }\n", - " fig.send_message('refresh', {});\n", - " };\n", - "\n", - " this.imageObj.onload = function () {\n", - " if (fig.image_mode === 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function () {\n", - " fig.ws.close();\n", - " };\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "};\n", - "\n", - "mpl.figure.prototype._init_header = function () {\n", - " var titlebar = document.createElement('div');\n", - " titlebar.classList =\n", - " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", - " var titletext = document.createElement('div');\n", - " titletext.classList = 'ui-dialog-title';\n", - " titletext.setAttribute(\n", - " 'style',\n", - " 'width: 100%; text-align: center; padding: 3px;'\n", - " );\n", - " titlebar.appendChild(titletext);\n", - " this.root.appendChild(titlebar);\n", - " this.header = titletext;\n", - "};\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", - "\n", - "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", - "\n", - "mpl.figure.prototype._init_canvas = function () {\n", - " var fig = this;\n", - "\n", - " var canvas_div = (this.canvas_div = document.createElement('div'));\n", - " canvas_div.setAttribute(\n", - " 'style',\n", - " 'border: 1px solid #ddd;' +\n", - " 'box-sizing: content-box;' +\n", - " 'clear: both;' +\n", - " 'min-height: 1px;' +\n", - " 'min-width: 1px;' +\n", - " 'outline: 0;' +\n", - " 'overflow: hidden;' +\n", - " 'position: relative;' +\n", - " 'resize: both;'\n", - " );\n", - "\n", - " function on_keyboard_event_closure(name) {\n", - " return function (event) {\n", - " return fig.key_event(event, name);\n", - " };\n", - " }\n", - "\n", - " canvas_div.addEventListener(\n", - " 'keydown',\n", - " on_keyboard_event_closure('key_press')\n", - " );\n", - " canvas_div.addEventListener(\n", - " 'keyup',\n", - " on_keyboard_event_closure('key_release')\n", - " );\n", - "\n", - " this._canvas_extra_style(canvas_div);\n", - " this.root.appendChild(canvas_div);\n", - "\n", - " var canvas = (this.canvas = document.createElement('canvas'));\n", - " canvas.classList.add('mpl-canvas');\n", - " canvas.setAttribute('style', 'box-sizing: content-box;');\n", - "\n", - " this.context = canvas.getContext('2d');\n", - "\n", - " var backingStore =\n", - " this.context.backingStorePixelRatio ||\n", - " this.context.webkitBackingStorePixelRatio ||\n", - " this.context.mozBackingStorePixelRatio ||\n", - " this.context.msBackingStorePixelRatio ||\n", - " this.context.oBackingStorePixelRatio ||\n", - " this.context.backingStorePixelRatio ||\n", - " 1;\n", - "\n", - " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", - " 'canvas'\n", - " ));\n", - " rubberband_canvas.setAttribute(\n", - " 'style',\n", - " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", - " );\n", - "\n", - " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", - " if (this.ResizeObserver === undefined) {\n", - " if (window.ResizeObserver !== undefined) {\n", - " this.ResizeObserver = window.ResizeObserver;\n", - " } else {\n", - " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", - " this.ResizeObserver = obs.ResizeObserver;\n", - " }\n", - " }\n", - "\n", - " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", - " var nentries = entries.length;\n", - " for (var i = 0; i < nentries; i++) {\n", - " var entry = entries[i];\n", - " var width, height;\n", - " if (entry.contentBoxSize) {\n", - " if (entry.contentBoxSize instanceof Array) {\n", - " // Chrome 84 implements new version of spec.\n", - " width = entry.contentBoxSize[0].inlineSize;\n", - " height = entry.contentBoxSize[0].blockSize;\n", - " } else {\n", - " // Firefox implements old version of spec.\n", - " width = entry.contentBoxSize.inlineSize;\n", - " height = entry.contentBoxSize.blockSize;\n", - " }\n", - " } else {\n", - " // Chrome <84 implements even older version of spec.\n", - " width = entry.contentRect.width;\n", - " height = entry.contentRect.height;\n", - " }\n", - "\n", - " // Keep the size of the canvas and rubber band canvas in sync with\n", - " // the canvas container.\n", - " if (entry.devicePixelContentBoxSize) {\n", - " // Chrome 84 implements new version of spec.\n", - " canvas.setAttribute(\n", - " 'width',\n", - " entry.devicePixelContentBoxSize[0].inlineSize\n", - " );\n", - " canvas.setAttribute(\n", - " 'height',\n", - " entry.devicePixelContentBoxSize[0].blockSize\n", - " );\n", - " } else {\n", - " canvas.setAttribute('width', width * fig.ratio);\n", - " canvas.setAttribute('height', height * fig.ratio);\n", - " }\n", - " canvas.setAttribute(\n", - " 'style',\n", - " 'width: ' + width + 'px; height: ' + height + 'px;'\n", - " );\n", - "\n", - " rubberband_canvas.setAttribute('width', width);\n", - " rubberband_canvas.setAttribute('height', height);\n", - "\n", - " // And update the size in Python. We ignore the initial 0/0 size\n", - " // that occurs as the element is placed into the DOM, which should\n", - " // otherwise not happen due to the minimum size styling.\n", - " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", - " fig.request_resize(width, height);\n", - " }\n", - " }\n", - " });\n", - " this.resizeObserverInstance.observe(canvas_div);\n", - "\n", - " function on_mouse_event_closure(name) {\n", - " return function (event) {\n", - " return fig.mouse_event(event, name);\n", - " };\n", - " }\n", - "\n", - " rubberband_canvas.addEventListener(\n", - " 'mousedown',\n", - " on_mouse_event_closure('button_press')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseup',\n", - " on_mouse_event_closure('button_release')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'dblclick',\n", - " on_mouse_event_closure('dblclick')\n", - " );\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband_canvas.addEventListener(\n", - " 'mousemove',\n", - " on_mouse_event_closure('motion_notify')\n", - " );\n", - "\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseenter',\n", - " on_mouse_event_closure('figure_enter')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseleave',\n", - " on_mouse_event_closure('figure_leave')\n", - " );\n", - "\n", - " canvas_div.addEventListener('wheel', function (event) {\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " on_mouse_event_closure('scroll')(event);\n", - " });\n", - "\n", - " canvas_div.appendChild(canvas);\n", - " canvas_div.appendChild(rubberband_canvas);\n", - "\n", - " this.rubberband_context = rubberband_canvas.getContext('2d');\n", - " this.rubberband_context.strokeStyle = '#000000';\n", - "\n", - " this._resize_canvas = function (width, height, forward) {\n", - " if (forward) {\n", - " canvas_div.style.width = width + 'px';\n", - " canvas_div.style.height = height + 'px';\n", - " }\n", - " };\n", - "\n", - " // Disable right mouse context menu.\n", - " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", - " event.preventDefault();\n", - " return false;\n", - " });\n", - "\n", - " function set_focus() {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "};\n", - "\n", - "mpl.figure.prototype._init_toolbar = function () {\n", - " var fig = this;\n", - "\n", - " var toolbar = document.createElement('div');\n", - " toolbar.classList = 'mpl-toolbar';\n", - " this.root.appendChild(toolbar);\n", - "\n", - " function on_click_closure(name) {\n", - " return function (_event) {\n", - " return fig.toolbar_button_onclick(name);\n", - " };\n", - " }\n", - "\n", - " function on_mouseover_closure(tooltip) {\n", - " return function (event) {\n", - " if (!event.currentTarget.disabled) {\n", - " return fig.toolbar_button_onmouseover(tooltip);\n", - " }\n", - " };\n", - " }\n", - "\n", - " fig.buttons = {};\n", - " var buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'mpl-button-group';\n", - " for (var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " /* Instead of a spacer, we start a new button group. */\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - " buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'mpl-button-group';\n", - " continue;\n", - " }\n", - "\n", - " var button = (fig.buttons[name] = document.createElement('button'));\n", - " button.classList = 'mpl-widget';\n", - " button.setAttribute('role', 'button');\n", - " button.setAttribute('aria-disabled', 'false');\n", - " button.addEventListener('click', on_click_closure(method_name));\n", - " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", - "\n", - " var icon_img = document.createElement('img');\n", - " icon_img.src = '_images/' + image + '.png';\n", - " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", - " icon_img.alt = tooltip;\n", - " button.appendChild(icon_img);\n", - "\n", - " buttonGroup.appendChild(button);\n", - " }\n", - "\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - "\n", - " var fmt_picker = document.createElement('select');\n", - " fmt_picker.classList = 'mpl-widget';\n", - " toolbar.appendChild(fmt_picker);\n", - " this.format_dropdown = fmt_picker;\n", - "\n", - " for (var ind in mpl.extensions) {\n", - " var fmt = mpl.extensions[ind];\n", - " var option = document.createElement('option');\n", - " option.selected = fmt === mpl.default_extension;\n", - " option.innerHTML = fmt;\n", - " fmt_picker.appendChild(option);\n", - " }\n", - "\n", - " var status_bar = document.createElement('span');\n", - " status_bar.classList = 'mpl-message';\n", - " toolbar.appendChild(status_bar);\n", - " this.message = status_bar;\n", - "};\n", - "\n", - "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", - " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", - " // which will in turn request a refresh of the image.\n", - " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", - "};\n", - "\n", - "mpl.figure.prototype.send_message = function (type, properties) {\n", - " properties['type'] = type;\n", - " properties['figure_id'] = this.id;\n", - " this.ws.send(JSON.stringify(properties));\n", - "};\n", - "\n", - "mpl.figure.prototype.send_draw_message = function () {\n", - " if (!this.waiting) {\n", - " this.waiting = true;\n", - " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", - " var format_dropdown = fig.format_dropdown;\n", - " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", - " fig.ondownload(fig, format);\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", - " var size = msg['size'];\n", - " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", - " fig._resize_canvas(size[0], size[1], msg['forward']);\n", - " fig.send_message('refresh', {});\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", - " var x0 = msg['x0'] / fig.ratio;\n", - " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", - " var x1 = msg['x1'] / fig.ratio;\n", - " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", - " x0 = Math.floor(x0) + 0.5;\n", - " y0 = Math.floor(y0) + 0.5;\n", - " x1 = Math.floor(x1) + 0.5;\n", - " y1 = Math.floor(y1) + 0.5;\n", - " var min_x = Math.min(x0, x1);\n", - " var min_y = Math.min(y0, y1);\n", - " var width = Math.abs(x1 - x0);\n", - " var height = Math.abs(y1 - y0);\n", - "\n", - " fig.rubberband_context.clearRect(\n", - " 0,\n", - " 0,\n", - " fig.canvas.width / fig.ratio,\n", - " fig.canvas.height / fig.ratio\n", - " );\n", - "\n", - " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", - " // Updates the figure title.\n", - " fig.header.textContent = msg['label'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", - " var cursor = msg['cursor'];\n", - " switch (cursor) {\n", - " case 0:\n", - " cursor = 'pointer';\n", - " break;\n", - " case 1:\n", - " cursor = 'default';\n", - " break;\n", - " case 2:\n", - " cursor = 'crosshair';\n", - " break;\n", - " case 3:\n", - " cursor = 'move';\n", - " break;\n", - " }\n", - " fig.rubberband_canvas.style.cursor = cursor;\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_message = function (fig, msg) {\n", - " fig.message.textContent = msg['message'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", - " // Request the server to send over a new figure.\n", - " fig.send_draw_message();\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", - " fig.image_mode = msg['mode'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", - " for (var key in msg) {\n", - " if (!(key in fig.buttons)) {\n", - " continue;\n", - " }\n", - " fig.buttons[key].disabled = !msg[key];\n", - " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", - " if (msg['mode'] === 'PAN') {\n", - " fig.buttons['Pan'].classList.add('active');\n", - " fig.buttons['Zoom'].classList.remove('active');\n", - " } else if (msg['mode'] === 'ZOOM') {\n", - " fig.buttons['Pan'].classList.remove('active');\n", - " fig.buttons['Zoom'].classList.add('active');\n", - " } else {\n", - " fig.buttons['Pan'].classList.remove('active');\n", - " fig.buttons['Zoom'].classList.remove('active');\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.updated_canvas_event = function () {\n", - " // Called whenever the canvas gets updated.\n", - " this.send_message('ack', {});\n", - "};\n", - "\n", - "// A function to construct a web socket function for onmessage handling.\n", - "// Called in the figure constructor.\n", - "mpl.figure.prototype._make_on_message_function = function (fig) {\n", - " return function socket_on_message(evt) {\n", - " if (evt.data instanceof Blob) {\n", - " var img = evt.data;\n", - " if (img.type !== 'image/png') {\n", - " /* FIXME: We get \"Resource interpreted as Image but\n", - " * transferred with MIME type text/plain:\" errors on\n", - " * Chrome. But how to set the MIME type? It doesn't seem\n", - " * to be part of the websocket stream */\n", - " img.type = 'image/png';\n", - " }\n", - "\n", - " /* Free the memory for the previous frames */\n", - " if (fig.imageObj.src) {\n", - " (window.URL || window.webkitURL).revokeObjectURL(\n", - " fig.imageObj.src\n", - " );\n", - " }\n", - "\n", - " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", - " img\n", - " );\n", - " fig.updated_canvas_event();\n", - " fig.waiting = false;\n", - " return;\n", - " } else if (\n", - " typeof evt.data === 'string' &&\n", - " evt.data.slice(0, 21) === 'data:image/png;base64'\n", - " ) {\n", - " fig.imageObj.src = evt.data;\n", - " fig.updated_canvas_event();\n", - " fig.waiting = false;\n", - " return;\n", - " }\n", - "\n", - " var msg = JSON.parse(evt.data);\n", - " var msg_type = msg['type'];\n", - "\n", - " // Call the \"handle_{type}\" callback, which takes\n", - " // the figure and JSON message as its only arguments.\n", - " try {\n", - " var callback = fig['handle_' + msg_type];\n", - " } catch (e) {\n", - " console.log(\n", - " \"No handler for the '\" + msg_type + \"' message type: \",\n", - " msg\n", - " );\n", - " return;\n", - " }\n", - "\n", - " if (callback) {\n", - " try {\n", - " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", - " callback(fig, msg);\n", - " } catch (e) {\n", - " console.log(\n", - " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", - " e,\n", - " e.stack,\n", - " msg\n", - " );\n", - " }\n", - " }\n", - " };\n", - "};\n", - "\n", - "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", - "mpl.findpos = function (e) {\n", - " //this section is from http://www.quirksmode.org/js/events_properties.html\n", - " var targ;\n", - " if (!e) {\n", - " e = window.event;\n", - " }\n", - " if (e.target) {\n", - " targ = e.target;\n", - " } else if (e.srcElement) {\n", - " targ = e.srcElement;\n", - " }\n", - " if (targ.nodeType === 3) {\n", - " // defeat Safari bug\n", - " targ = targ.parentNode;\n", - " }\n", - "\n", - " // pageX,Y are the mouse positions relative to the document\n", - " var boundingRect = targ.getBoundingClientRect();\n", - " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", - " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", - "\n", - " return { x: x, y: y };\n", - "};\n", - "\n", - "/*\n", - " * return a copy of an object with only non-object keys\n", - " * we need this to avoid circular references\n", - " * http://stackoverflow.com/a/24161582/3208463\n", - " */\n", - "function simpleKeys(original) {\n", - " return Object.keys(original).reduce(function (obj, key) {\n", - " if (typeof original[key] !== 'object') {\n", - " obj[key] = original[key];\n", - " }\n", - " return obj;\n", - " }, {});\n", - "}\n", - "\n", - "mpl.figure.prototype.mouse_event = function (event, name) {\n", - " var canvas_pos = mpl.findpos(event);\n", - "\n", - " if (name === 'button_press') {\n", - " this.canvas.focus();\n", - " this.canvas_div.focus();\n", - " }\n", - "\n", - " var x = canvas_pos.x * this.ratio;\n", - " var y = canvas_pos.y * this.ratio;\n", - "\n", - " this.send_message(name, {\n", - " x: x,\n", - " y: y,\n", - " button: event.button,\n", - " step: event.step,\n", - " guiEvent: simpleKeys(event),\n", - " });\n", - "\n", - " /* This prevents the web browser from automatically changing to\n", - " * the text insertion cursor when the button is pressed. We want\n", - " * to control all of the cursor setting manually through the\n", - " * 'cursor' event from matplotlib */\n", - " event.preventDefault();\n", - " return false;\n", - "};\n", - "\n", - "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", - " // Handle any extra behaviour associated with a key event\n", - "};\n", - "\n", - "mpl.figure.prototype.key_event = function (event, name) {\n", - " // Prevent repeat events\n", - " if (name === 'key_press') {\n", - " if (event.key === this._key) {\n", - " return;\n", - " } else {\n", - " this._key = event.key;\n", - " }\n", - " }\n", - " if (name === 'key_release') {\n", - " this._key = null;\n", - " }\n", - "\n", - " var value = '';\n", - " if (event.ctrlKey && event.key !== 'Control') {\n", - " value += 'ctrl+';\n", - " }\n", - " else if (event.altKey && event.key !== 'Alt') {\n", - " value += 'alt+';\n", - " }\n", - " else if (event.shiftKey && event.key !== 'Shift') {\n", - " value += 'shift+';\n", - " }\n", - "\n", - " value += 'k' + event.key;\n", - "\n", - " this._key_event_extra(event, name);\n", - "\n", - " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", - " return false;\n", - "};\n", - "\n", - "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", - " if (name === 'download') {\n", - " this.handle_save(this, null);\n", - " } else {\n", - " this.send_message('toolbar_button', { name: name });\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", - " this.message.textContent = tooltip;\n", - "};\n", - "\n", - "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", - "// prettier-ignore\n", - "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", - "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", - "\n", - "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", - "\n", - "mpl.default_extension = \"png\";/* global mpl */\n", - "\n", - "var comm_websocket_adapter = function (comm) {\n", - " // Create a \"websocket\"-like object which calls the given IPython comm\n", - " // object with the appropriate methods. Currently this is a non binary\n", - " // socket, so there is still some room for performance tuning.\n", - " var ws = {};\n", - "\n", - " ws.binaryType = comm.kernel.ws.binaryType;\n", - " ws.readyState = comm.kernel.ws.readyState;\n", - " function updateReadyState(_event) {\n", - " if (comm.kernel.ws) {\n", - " ws.readyState = comm.kernel.ws.readyState;\n", - " } else {\n", - " ws.readyState = 3; // Closed state.\n", - " }\n", - " }\n", - " comm.kernel.ws.addEventListener('open', updateReadyState);\n", - " comm.kernel.ws.addEventListener('close', updateReadyState);\n", - " comm.kernel.ws.addEventListener('error', updateReadyState);\n", - "\n", - " ws.close = function () {\n", - " comm.close();\n", - " };\n", - " ws.send = function (m) {\n", - " //console.log('sending', m);\n", - " comm.send(m);\n", - " };\n", - " // Register the callback with on_msg.\n", - " comm.on_msg(function (msg) {\n", - " //console.log('receiving', msg['content']['data'], msg);\n", - " var data = msg['content']['data'];\n", - " if (data['blob'] !== undefined) {\n", - " data = {\n", - " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", - " };\n", - " }\n", - " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", - " ws.onmessage(data);\n", - " });\n", - " return ws;\n", - "};\n", - "\n", - "mpl.mpl_figure_comm = function (comm, msg) {\n", - " // This is the function which gets called when the mpl process\n", - " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", - "\n", - " var id = msg.content.data.id;\n", - " // Get hold of the div created by the display call when the Comm\n", - " // socket was opened in Python.\n", - " var element = document.getElementById(id);\n", - " var ws_proxy = comm_websocket_adapter(comm);\n", - "\n", - " function ondownload(figure, _format) {\n", - " window.open(figure.canvas.toDataURL());\n", - " }\n", - "\n", - " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", - "\n", - " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", - " // web socket which is closed, not our websocket->open comm proxy.\n", - " ws_proxy.onopen();\n", - "\n", - " fig.parent_element = element;\n", - " fig.cell_info = mpl.find_output_cell(\"
\");\n", - " if (!fig.cell_info) {\n", - " console.error('Failed to find cell for figure', id, fig);\n", - " return;\n", - " }\n", - " fig.cell_info[0].output_area.element.on(\n", - " 'cleared',\n", - " { fig: fig },\n", - " fig._remove_fig_handler\n", - " );\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_close = function (fig, msg) {\n", - " var width = fig.canvas.width / fig.ratio;\n", - " fig.cell_info[0].output_area.element.off(\n", - " 'cleared',\n", - " fig._remove_fig_handler\n", - " );\n", - " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", - "\n", - " // Update the output cell to use the data from the current canvas.\n", - " fig.push_to_output();\n", - " var dataURL = fig.canvas.toDataURL();\n", - " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", - " // the notebook keyboard shortcuts fail.\n", - " IPython.keyboard_manager.enable();\n", - " fig.parent_element.innerHTML =\n", - " '';\n", - " fig.close_ws(fig, msg);\n", - "};\n", - "\n", - "mpl.figure.prototype.close_ws = function (fig, msg) {\n", - " fig.send_message('closing', msg);\n", - " // fig.ws.close()\n", - "};\n", - "\n", - "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", - " // Turn the data on the canvas into data in the output cell.\n", - " var width = this.canvas.width / this.ratio;\n", - " var dataURL = this.canvas.toDataURL();\n", - " this.cell_info[1]['text/html'] =\n", - " '';\n", - "};\n", - "\n", - "mpl.figure.prototype.updated_canvas_event = function () {\n", - " // Tell IPython that the notebook contents must change.\n", - " IPython.notebook.set_dirty(true);\n", - " this.send_message('ack', {});\n", - " var fig = this;\n", - " // Wait a second, then push the new image to the DOM so\n", - " // that it is saved nicely (might be nice to debounce this).\n", - " setTimeout(function () {\n", - " fig.push_to_output();\n", - " }, 1000);\n", - "};\n", - "\n", - "mpl.figure.prototype._init_toolbar = function () {\n", - " var fig = this;\n", - "\n", - " var toolbar = document.createElement('div');\n", - " toolbar.classList = 'btn-toolbar';\n", - " this.root.appendChild(toolbar);\n", - "\n", - " function on_click_closure(name) {\n", - " return function (_event) {\n", - " return fig.toolbar_button_onclick(name);\n", - " };\n", - " }\n", - "\n", - " function on_mouseover_closure(tooltip) {\n", - " return function (event) {\n", - " if (!event.currentTarget.disabled) {\n", - " return fig.toolbar_button_onmouseover(tooltip);\n", - " }\n", - " };\n", - " }\n", - "\n", - " fig.buttons = {};\n", - " var buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'btn-group';\n", - " var button;\n", - " for (var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " /* Instead of a spacer, we start a new button group. */\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - " buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'btn-group';\n", - " continue;\n", - " }\n", - "\n", - " button = fig.buttons[name] = document.createElement('button');\n", - " button.classList = 'btn btn-default';\n", - " button.href = '#';\n", - " button.title = name;\n", - " button.innerHTML = '';\n", - " button.addEventListener('click', on_click_closure(method_name));\n", - " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", - " buttonGroup.appendChild(button);\n", - " }\n", - "\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - "\n", - " // Add the status bar.\n", - " var status_bar = document.createElement('span');\n", - " status_bar.classList = 'mpl-message pull-right';\n", - " toolbar.appendChild(status_bar);\n", - " this.message = status_bar;\n", - "\n", - " // Add the close button to the window.\n", - " var buttongrp = document.createElement('div');\n", - " buttongrp.classList = 'btn-group inline pull-right';\n", - " button = document.createElement('button');\n", - " button.classList = 'btn btn-mini btn-primary';\n", - " button.href = '#';\n", - " button.title = 'Stop Interaction';\n", - " button.innerHTML = '';\n", - " button.addEventListener('click', function (_evt) {\n", - " fig.handle_close(fig, {});\n", - " });\n", - " button.addEventListener(\n", - " 'mouseover',\n", - " on_mouseover_closure('Stop Interaction')\n", - " );\n", - " buttongrp.appendChild(button);\n", - " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", - " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", - "};\n", - "\n", - "mpl.figure.prototype._remove_fig_handler = function (event) {\n", - " var fig = event.data.fig;\n", - " if (event.target !== this) {\n", - " // Ignore bubbled events from children.\n", - " return;\n", - " }\n", - " fig.close_ws(fig, {});\n", - "};\n", - "\n", - "mpl.figure.prototype._root_extra_style = function (el) {\n", - " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", - "};\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function (el) {\n", - " // this is important to make the div 'focusable\n", - " el.setAttribute('tabindex', 0);\n", - " // reach out to IPython and tell the keyboard manager to turn it's self\n", - " // off when our div gets focus\n", - "\n", - " // location in version 3\n", - " if (IPython.notebook.keyboard_manager) {\n", - " IPython.notebook.keyboard_manager.register_events(el);\n", - " } else {\n", - " // location in version 2\n", - " IPython.keyboard_manager.register_events(el);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", - " var manager = IPython.notebook.keyboard_manager;\n", - " if (!manager) {\n", - " manager = IPython.keyboard_manager;\n", - " }\n", - "\n", - " // Check for shift+enter\n", - " if (event.shiftKey && event.which === 13) {\n", - " this.canvas_div.blur();\n", - " // select the cell after this one\n", - " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", - " IPython.notebook.select(index + 1);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", - " fig.ondownload(fig, null);\n", - "};\n", - "\n", - "mpl.find_output_cell = function (html_output) {\n", - " // Return the cell and output element which can be found *uniquely* in the notebook.\n", - " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", - " // IPython event is triggered only after the cells have been serialised, which for\n", - " // our purposes (turning an active figure into a static one), is too late.\n", - " var cells = IPython.notebook.get_cells();\n", - " var ncells = cells.length;\n", - " for (var i = 0; i < ncells; i++) {\n", - " var cell = cells[i];\n", - " if (cell.cell_type === 'code') {\n", - " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", - " var data = cell.output_area.outputs[j];\n", - " if (data.data) {\n", - " // IPython >= 3 moved mimebundle to data attribute of output\n", - " data = data.data;\n", - " }\n", - " if (data['text/html'] === html_output) {\n", - " return [cell, data, j];\n", - " }\n", - " }\n", - " }\n", - " }\n", - "};\n", - "\n", - "// Register the function which deals with the matplotlib target/channel.\n", - "// The kernel may be null if the page has been refreshed.\n", - "if (IPython.notebook.kernel !== null) {\n", - " IPython.notebook.kernel.comm_manager.register_target(\n", - " 'matplotlib',\n", - " mpl.mpl_figure_comm\n", - " );\n", - "}\n" - ], + "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.get_websocket_type = function () {\n if (typeof WebSocket !== 'undefined') {\n return WebSocket;\n } else if (typeof MozWebSocket !== 'undefined') {\n return MozWebSocket;\n } else {\n alert(\n 'Your browser does not have WebSocket support. ' +\n 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n 'Firefox 4 and 5 are also supported but you ' +\n 'have to enable WebSockets in about:config.'\n );\n }\n};\n\nmpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n this.id = figure_id;\n\n this.ws = websocket;\n\n this.supports_binary = this.ws.binaryType !== undefined;\n\n if (!this.supports_binary) {\n var warnings = document.getElementById('mpl-warnings');\n if (warnings) {\n warnings.style.display = 'block';\n warnings.textContent =\n 'This browser does not support binary websocket messages. ' +\n 'Performance may be slow.';\n }\n }\n\n this.imageObj = new Image();\n\n this.context = undefined;\n this.message = undefined;\n this.canvas = undefined;\n this.rubberband_canvas = undefined;\n this.rubberband_context = undefined;\n this.format_dropdown = undefined;\n\n this.image_mode = 'full';\n\n this.root = document.createElement('div');\n this.root.setAttribute('style', 'display: inline-block');\n this._root_extra_style(this.root);\n\n parent_element.appendChild(this.root);\n\n this._init_header(this);\n this._init_canvas(this);\n this._init_toolbar(this);\n\n var fig = this;\n\n this.waiting = false;\n\n this.ws.onopen = function () {\n fig.send_message('supports_binary', { value: fig.supports_binary });\n fig.send_message('send_image_mode', {});\n if (fig.ratio !== 1) {\n fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n }\n fig.send_message('refresh', {});\n };\n\n this.imageObj.onload = function () {\n if (fig.image_mode === 'full') {\n // Full images could contain transparency (where diff images\n // almost always do), so we need to clear the canvas so that\n // there is no ghosting.\n fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n }\n fig.context.drawImage(fig.imageObj, 0, 0);\n };\n\n this.imageObj.onunload = function () {\n fig.ws.close();\n };\n\n this.ws.onmessage = this._make_on_message_function(this);\n\n this.ondownload = ondownload;\n};\n\nmpl.figure.prototype._init_header = function () {\n var titlebar = document.createElement('div');\n titlebar.classList =\n 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n var titletext = document.createElement('div');\n titletext.classList = 'ui-dialog-title';\n titletext.setAttribute(\n 'style',\n 'width: 100%; text-align: center; padding: 3px;'\n );\n titlebar.appendChild(titletext);\n this.root.appendChild(titlebar);\n this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n var fig = this;\n\n var canvas_div = (this.canvas_div = document.createElement('div'));\n canvas_div.setAttribute(\n 'style',\n 'border: 1px solid #ddd;' +\n 'box-sizing: content-box;' +\n 'clear: both;' +\n 'min-height: 1px;' +\n 'min-width: 1px;' +\n 'outline: 0;' +\n 'overflow: hidden;' +\n 'position: relative;' +\n 'resize: both;'\n );\n\n function on_keyboard_event_closure(name) {\n return function (event) {\n return fig.key_event(event, name);\n };\n }\n\n canvas_div.addEventListener(\n 'keydown',\n on_keyboard_event_closure('key_press')\n );\n canvas_div.addEventListener(\n 'keyup',\n on_keyboard_event_closure('key_release')\n );\n\n this._canvas_extra_style(canvas_div);\n this.root.appendChild(canvas_div);\n\n var canvas = (this.canvas = document.createElement('canvas'));\n canvas.classList.add('mpl-canvas');\n canvas.setAttribute('style', 'box-sizing: content-box;');\n\n this.context = canvas.getContext('2d');\n\n var backingStore =\n this.context.backingStorePixelRatio ||\n this.context.webkitBackingStorePixelRatio ||\n this.context.mozBackingStorePixelRatio ||\n this.context.msBackingStorePixelRatio ||\n this.context.oBackingStorePixelRatio ||\n this.context.backingStorePixelRatio ||\n 1;\n\n this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n 'canvas'\n ));\n rubberband_canvas.setAttribute(\n 'style',\n 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n );\n\n // Apply a ponyfill if ResizeObserver is not implemented by browser.\n if (this.ResizeObserver === undefined) {\n if (window.ResizeObserver !== undefined) {\n this.ResizeObserver = window.ResizeObserver;\n } else {\n var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n this.ResizeObserver = obs.ResizeObserver;\n }\n }\n\n this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n var nentries = entries.length;\n for (var i = 0; i < nentries; i++) {\n var entry = entries[i];\n var width, height;\n if (entry.contentBoxSize) {\n if (entry.contentBoxSize instanceof Array) {\n // Chrome 84 implements new version of spec.\n width = entry.contentBoxSize[0].inlineSize;\n height = entry.contentBoxSize[0].blockSize;\n } else {\n // Firefox implements old version of spec.\n width = entry.contentBoxSize.inlineSize;\n height = entry.contentBoxSize.blockSize;\n }\n } else {\n // Chrome <84 implements even older version of spec.\n width = entry.contentRect.width;\n height = entry.contentRect.height;\n }\n\n // Keep the size of the canvas and rubber band canvas in sync with\n // the canvas container.\n if (entry.devicePixelContentBoxSize) {\n // Chrome 84 implements new version of spec.\n canvas.setAttribute(\n 'width',\n entry.devicePixelContentBoxSize[0].inlineSize\n );\n canvas.setAttribute(\n 'height',\n entry.devicePixelContentBoxSize[0].blockSize\n );\n } else {\n canvas.setAttribute('width', width * fig.ratio);\n canvas.setAttribute('height', height * fig.ratio);\n }\n canvas.setAttribute(\n 'style',\n 'width: ' + width + 'px; height: ' + height + 'px;'\n );\n\n rubberband_canvas.setAttribute('width', width);\n rubberband_canvas.setAttribute('height', height);\n\n // And update the size in Python. We ignore the initial 0/0 size\n // that occurs as the element is placed into the DOM, which should\n // otherwise not happen due to the minimum size styling.\n if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n fig.request_resize(width, height);\n }\n }\n });\n this.resizeObserverInstance.observe(canvas_div);\n\n function on_mouse_event_closure(name) {\n return function (event) {\n return fig.mouse_event(event, name);\n };\n }\n\n rubberband_canvas.addEventListener(\n 'mousedown',\n on_mouse_event_closure('button_press')\n );\n rubberband_canvas.addEventListener(\n 'mouseup',\n on_mouse_event_closure('button_release')\n );\n rubberband_canvas.addEventListener(\n 'dblclick',\n on_mouse_event_closure('dblclick')\n );\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband_canvas.addEventListener(\n 'mousemove',\n on_mouse_event_closure('motion_notify')\n );\n\n rubberband_canvas.addEventListener(\n 'mouseenter',\n on_mouse_event_closure('figure_enter')\n );\n rubberband_canvas.addEventListener(\n 'mouseleave',\n on_mouse_event_closure('figure_leave')\n );\n\n canvas_div.addEventListener('wheel', function (event) {\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n on_mouse_event_closure('scroll')(event);\n });\n\n canvas_div.appendChild(canvas);\n canvas_div.appendChild(rubberband_canvas);\n\n this.rubberband_context = rubberband_canvas.getContext('2d');\n this.rubberband_context.strokeStyle = '#000000';\n\n this._resize_canvas = function (width, height, forward) {\n if (forward) {\n canvas_div.style.width = width + 'px';\n canvas_div.style.height = height + 'px';\n }\n };\n\n // Disable right mouse context menu.\n this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n event.preventDefault();\n return false;\n });\n\n function set_focus() {\n canvas.focus();\n canvas_div.focus();\n }\n\n window.setTimeout(set_focus, 100);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'mpl-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n for (var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n continue;\n }\n\n var button = (fig.buttons[name] = document.createElement('button'));\n button.classList = 'mpl-widget';\n button.setAttribute('role', 'button');\n button.setAttribute('aria-disabled', 'false');\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n var icon_img = document.createElement('img');\n icon_img.src = '_images/' + image + '.png';\n icon_img.srcset = '_images/' + image + '_large.png 2x';\n icon_img.alt = tooltip;\n button.appendChild(icon_img);\n\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n var fmt_picker = document.createElement('select');\n fmt_picker.classList = 'mpl-widget';\n toolbar.appendChild(fmt_picker);\n this.format_dropdown = fmt_picker;\n\n for (var ind in mpl.extensions) {\n var fmt = mpl.extensions[ind];\n var option = document.createElement('option');\n option.selected = fmt === mpl.default_extension;\n option.innerHTML = fmt;\n fmt_picker.appendChild(option);\n }\n\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n};\n\nmpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n // which will in turn request a refresh of the image.\n this.send_message('resize', { width: x_pixels, height: y_pixels });\n};\n\nmpl.figure.prototype.send_message = function (type, properties) {\n properties['type'] = type;\n properties['figure_id'] = this.id;\n this.ws.send(JSON.stringify(properties));\n};\n\nmpl.figure.prototype.send_draw_message = function () {\n if (!this.waiting) {\n this.waiting = true;\n this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n var format_dropdown = fig.format_dropdown;\n var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n fig.ondownload(fig, format);\n};\n\nmpl.figure.prototype.handle_resize = function (fig, msg) {\n var size = msg['size'];\n if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n fig._resize_canvas(size[0], size[1], msg['forward']);\n fig.send_message('refresh', {});\n }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n var x0 = msg['x0'] / fig.ratio;\n var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n var x1 = msg['x1'] / fig.ratio;\n var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n x0 = Math.floor(x0) + 0.5;\n y0 = Math.floor(y0) + 0.5;\n x1 = Math.floor(x1) + 0.5;\n y1 = Math.floor(y1) + 0.5;\n var min_x = Math.min(x0, x1);\n var min_y = Math.min(y0, y1);\n var width = Math.abs(x1 - x0);\n var height = Math.abs(y1 - y0);\n\n fig.rubberband_context.clearRect(\n 0,\n 0,\n fig.canvas.width / fig.ratio,\n fig.canvas.height / fig.ratio\n );\n\n fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n var cursor = msg['cursor'];\n switch (cursor) {\n case 0:\n cursor = 'pointer';\n break;\n case 1:\n cursor = 'default';\n break;\n case 2:\n cursor = 'crosshair';\n break;\n case 3:\n cursor = 'move';\n break;\n }\n fig.rubberband_canvas.style.cursor = cursor;\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n for (var key in msg) {\n if (!(key in fig.buttons)) {\n continue;\n }\n fig.buttons[key].disabled = !msg[key];\n fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n if (msg['mode'] === 'PAN') {\n fig.buttons['Pan'].classList.add('active');\n fig.buttons['Zoom'].classList.remove('active');\n } else if (msg['mode'] === 'ZOOM') {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.add('active');\n } else {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.remove('active');\n }\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n // Called whenever the canvas gets updated.\n this.send_message('ack', {});\n};\n\n// A function to construct a web socket function for onmessage handling.\n// Called in the figure constructor.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n return function socket_on_message(evt) {\n if (evt.data instanceof Blob) {\n var img = evt.data;\n if (img.type !== 'image/png') {\n /* FIXME: We get \"Resource interpreted as Image but\n * transferred with MIME type text/plain:\" errors on\n * Chrome. But how to set the MIME type? It doesn't seem\n * to be part of the websocket stream */\n img.type = 'image/png';\n }\n\n /* Free the memory for the previous frames */\n if (fig.imageObj.src) {\n (window.URL || window.webkitURL).revokeObjectURL(\n fig.imageObj.src\n );\n }\n\n fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n img\n );\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n } else if (\n typeof evt.data === 'string' &&\n evt.data.slice(0, 21) === 'data:image/png;base64'\n ) {\n fig.imageObj.src = evt.data;\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n }\n\n var msg = JSON.parse(evt.data);\n var msg_type = msg['type'];\n\n // Call the \"handle_{type}\" callback, which takes\n // the figure and JSON message as its only arguments.\n try {\n var callback = fig['handle_' + msg_type];\n } catch (e) {\n console.log(\n \"No handler for the '\" + msg_type + \"' message type: \",\n msg\n );\n return;\n }\n\n if (callback) {\n try {\n // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n callback(fig, msg);\n } catch (e) {\n console.log(\n \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n e,\n e.stack,\n msg\n );\n }\n }\n };\n};\n\n// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.findpos = function (e) {\n //this section is from http://www.quirksmode.org/js/events_properties.html\n var targ;\n if (!e) {\n e = window.event;\n }\n if (e.target) {\n targ = e.target;\n } else if (e.srcElement) {\n targ = e.srcElement;\n }\n if (targ.nodeType === 3) {\n // defeat Safari bug\n targ = targ.parentNode;\n }\n\n // pageX,Y are the mouse positions relative to the document\n var boundingRect = targ.getBoundingClientRect();\n var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n\n return { x: x, y: y };\n};\n\n/*\n * return a copy of an object with only non-object keys\n * we need this to avoid circular references\n * http://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n return Object.keys(original).reduce(function (obj, key) {\n if (typeof original[key] !== 'object') {\n obj[key] = original[key];\n }\n return obj;\n }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n var canvas_pos = mpl.findpos(event);\n\n if (name === 'button_press') {\n this.canvas.focus();\n this.canvas_div.focus();\n }\n\n var x = canvas_pos.x * this.ratio;\n var y = canvas_pos.y * this.ratio;\n\n this.send_message(name, {\n x: x,\n y: y,\n button: event.button,\n step: event.step,\n guiEvent: simpleKeys(event),\n });\n\n /* This prevents the web browser from automatically changing to\n * the text insertion cursor when the button is pressed. We want\n * to control all of the cursor setting manually through the\n * 'cursor' event from matplotlib */\n event.preventDefault();\n return false;\n};\n\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n // Prevent repeat events\n if (name === 'key_press') {\n if (event.key === this._key) {\n return;\n } else {\n this._key = event.key;\n }\n }\n if (name === 'key_release') {\n this._key = null;\n }\n\n var value = '';\n if (event.ctrlKey && event.key !== 'Control') {\n value += 'ctrl+';\n }\n else if (event.altKey && event.key !== 'Alt') {\n value += 'alt+';\n }\n else if (event.shiftKey && event.key !== 'Shift') {\n value += 'shift+';\n }\n\n value += 'k' + event.key;\n\n this._key_event_extra(event, name);\n\n this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n return false;\n};\n\nmpl.figure.prototype.toolbar_button_onclick = function (name) {\n if (name === 'download') {\n this.handle_save(this, null);\n } else {\n this.send_message('toolbar_button', { name: name });\n }\n};\n\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar comm_websocket_adapter = function (comm) {\n // Create a \"websocket\"-like object which calls the given IPython comm\n // object with the appropriate methods. Currently this is a non binary\n // socket, so there is still some room for performance tuning.\n var ws = {};\n\n ws.binaryType = comm.kernel.ws.binaryType;\n ws.readyState = comm.kernel.ws.readyState;\n function updateReadyState(_event) {\n if (comm.kernel.ws) {\n ws.readyState = comm.kernel.ws.readyState;\n } else {\n ws.readyState = 3; // Closed state.\n }\n }\n comm.kernel.ws.addEventListener('open', updateReadyState);\n comm.kernel.ws.addEventListener('close', updateReadyState);\n comm.kernel.ws.addEventListener('error', updateReadyState);\n\n ws.close = function () {\n comm.close();\n };\n ws.send = function (m) {\n //console.log('sending', m);\n comm.send(m);\n };\n // Register the callback with on_msg.\n comm.on_msg(function (msg) {\n //console.log('receiving', msg['content']['data'], msg);\n var data = msg['content']['data'];\n if (data['blob'] !== undefined) {\n data = {\n data: new Blob(msg['buffers'], { type: data['blob'] }),\n };\n }\n // Pass the mpl event to the overridden (by mpl) onmessage function.\n ws.onmessage(data);\n });\n return ws;\n};\n\nmpl.mpl_figure_comm = function (comm, msg) {\n // This is the function which gets called when the mpl process\n // starts-up an IPython Comm through the \"matplotlib\" channel.\n\n var id = msg.content.data.id;\n // Get hold of the div created by the display call when the Comm\n // socket was opened in Python.\n var element = document.getElementById(id);\n var ws_proxy = comm_websocket_adapter(comm);\n\n function ondownload(figure, _format) {\n window.open(figure.canvas.toDataURL());\n }\n\n var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n\n // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n // web socket which is closed, not our websocket->open comm proxy.\n ws_proxy.onopen();\n\n fig.parent_element = element;\n fig.cell_info = mpl.find_output_cell(\"
\");\n if (!fig.cell_info) {\n console.error('Failed to find cell for figure', id, fig);\n return;\n }\n fig.cell_info[0].output_area.element.on(\n 'cleared',\n { fig: fig },\n fig._remove_fig_handler\n );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n var width = fig.canvas.width / fig.ratio;\n fig.cell_info[0].output_area.element.off(\n 'cleared',\n fig._remove_fig_handler\n );\n fig.resizeObserverInstance.unobserve(fig.canvas_div);\n\n // Update the output cell to use the data from the current canvas.\n fig.push_to_output();\n var dataURL = fig.canvas.toDataURL();\n // Re-enable the keyboard manager in IPython - without this line, in FF,\n // the notebook keyboard shortcuts fail.\n IPython.keyboard_manager.enable();\n fig.parent_element.innerHTML =\n '';\n fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n fig.send_message('closing', msg);\n // fig.ws.close()\n};\n\nmpl.figure.prototype.push_to_output = function (_remove_interactive) {\n // Turn the data on the canvas into data in the output cell.\n var width = this.canvas.width / this.ratio;\n var dataURL = this.canvas.toDataURL();\n this.cell_info[1]['text/html'] =\n '';\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n // Tell IPython that the notebook contents must change.\n IPython.notebook.set_dirty(true);\n this.send_message('ack', {});\n var fig = this;\n // Wait a second, then push the new image to the DOM so\n // that it is saved nicely (might be nice to debounce this).\n setTimeout(function () {\n fig.push_to_output();\n }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'btn-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n var button;\n for (var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n continue;\n }\n\n button = fig.buttons[name] = document.createElement('button');\n button.classList = 'btn btn-default';\n button.href = '#';\n button.title = name;\n button.innerHTML = '';\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n // Add the status bar.\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message pull-right';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n\n // Add the close button to the window.\n var buttongrp = document.createElement('div');\n buttongrp.classList = 'btn-group inline pull-right';\n button = document.createElement('button');\n button.classList = 'btn btn-mini btn-primary';\n button.href = '#';\n button.title = 'Stop Interaction';\n button.innerHTML = '';\n button.addEventListener('click', function (_evt) {\n fig.handle_close(fig, {});\n });\n button.addEventListener(\n 'mouseover',\n on_mouseover_closure('Stop Interaction')\n );\n buttongrp.appendChild(button);\n var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n var fig = event.data.fig;\n if (event.target !== this) {\n // Ignore bubbled events from children.\n return;\n }\n fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n // this is important to make the div 'focusable\n el.setAttribute('tabindex', 0);\n // reach out to IPython and tell the keyboard manager to turn it's self\n // off when our div gets focus\n\n // location in version 3\n if (IPython.notebook.keyboard_manager) {\n IPython.notebook.keyboard_manager.register_events(el);\n } else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n var manager = IPython.notebook.keyboard_manager;\n if (!manager) {\n manager = IPython.keyboard_manager;\n }\n\n // Check for shift+enter\n if (event.shiftKey && event.which === 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n fig.ondownload(fig, null);\n};\n\nmpl.find_output_cell = function (html_output) {\n // Return the cell and output element which can be found *uniquely* in the notebook.\n // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n // IPython event is triggered only after the cells have been serialised, which for\n // our purposes (turning an active figure into a static one), is too late.\n var cells = IPython.notebook.get_cells();\n var ncells = cells.length;\n for (var i = 0; i < ncells; i++) {\n var cell = cells[i];\n if (cell.cell_type === 'code') {\n for (var j = 0; j < cell.output_area.outputs.length; j++) {\n var data = cell.output_area.outputs[j];\n if (data.data) {\n // IPython >= 3 moved mimebundle to data attribute of output\n data = data.data;\n }\n if (data['text/html'] === html_output) {\n return [cell, data, j];\n }\n }\n }\n }\n};\n\n// Register the function which deals with the matplotlib target/channel.\n// The kernel may be null if the page has been refreshed.\nif (IPython.notebook.kernel !== null) {\n IPython.notebook.kernel.comm_manager.register_target(\n 'matplotlib',\n mpl.mpl_figure_comm\n );\n}\n", "text/plain": [ "" ] @@ -1926,6 +963,15 @@ "You can take a look at a test trajectory to see how well your trained agent is able to control the currents to follow the test trajectory. For the agent to decide for an action given an observation you can just call its ```predict()``` function. The key deterministic is important so that the agent is not using a stochastic policy like epsilon greedy but is instead chosing an action greedily. The ```env.render()``` will then visualize the agent's and reference generator's trajectories as well as the reward." ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "motor_dashboard.initialize()" + ] + }, { "cell_type": "code", "execution_count": 51, @@ -1933,980 +979,7 @@ "outputs": [ { "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "/* global mpl */\n", - "window.mpl = {};\n", - "\n", - "mpl.get_websocket_type = function () {\n", - " if (typeof WebSocket !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof MozWebSocket !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert(\n", - " 'Your browser does not have WebSocket support. ' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.'\n", - " );\n", - " }\n", - "};\n", - "\n", - "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = this.ws.binaryType !== undefined;\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById('mpl-warnings');\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent =\n", - " 'This browser does not support binary websocket messages. ' +\n", - " 'Performance may be slow.';\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = document.createElement('div');\n", - " this.root.setAttribute('style', 'display: inline-block');\n", - " this._root_extra_style(this.root);\n", - "\n", - " parent_element.appendChild(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message('supports_binary', { value: fig.supports_binary });\n", - " fig.send_message('send_image_mode', {});\n", - " if (fig.ratio !== 1) {\n", - " fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n", - " }\n", - " fig.send_message('refresh', {});\n", - " };\n", - "\n", - " this.imageObj.onload = function () {\n", - " if (fig.image_mode === 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function () {\n", - " fig.ws.close();\n", - " };\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "};\n", - "\n", - "mpl.figure.prototype._init_header = function () {\n", - " var titlebar = document.createElement('div');\n", - " titlebar.classList =\n", - " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", - " var titletext = document.createElement('div');\n", - " titletext.classList = 'ui-dialog-title';\n", - " titletext.setAttribute(\n", - " 'style',\n", - " 'width: 100%; text-align: center; padding: 3px;'\n", - " );\n", - " titlebar.appendChild(titletext);\n", - " this.root.appendChild(titlebar);\n", - " this.header = titletext;\n", - "};\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", - "\n", - "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", - "\n", - "mpl.figure.prototype._init_canvas = function () {\n", - " var fig = this;\n", - "\n", - " var canvas_div = (this.canvas_div = document.createElement('div'));\n", - " canvas_div.setAttribute(\n", - " 'style',\n", - " 'border: 1px solid #ddd;' +\n", - " 'box-sizing: content-box;' +\n", - " 'clear: both;' +\n", - " 'min-height: 1px;' +\n", - " 'min-width: 1px;' +\n", - " 'outline: 0;' +\n", - " 'overflow: hidden;' +\n", - " 'position: relative;' +\n", - " 'resize: both;'\n", - " );\n", - "\n", - " function on_keyboard_event_closure(name) {\n", - " return function (event) {\n", - " return fig.key_event(event, name);\n", - " };\n", - " }\n", - "\n", - " canvas_div.addEventListener(\n", - " 'keydown',\n", - " on_keyboard_event_closure('key_press')\n", - " );\n", - " canvas_div.addEventListener(\n", - " 'keyup',\n", - " on_keyboard_event_closure('key_release')\n", - " );\n", - "\n", - " this._canvas_extra_style(canvas_div);\n", - " this.root.appendChild(canvas_div);\n", - "\n", - " var canvas = (this.canvas = document.createElement('canvas'));\n", - " canvas.classList.add('mpl-canvas');\n", - " canvas.setAttribute('style', 'box-sizing: content-box;');\n", - "\n", - " this.context = canvas.getContext('2d');\n", - "\n", - " var backingStore =\n", - " this.context.backingStorePixelRatio ||\n", - " this.context.webkitBackingStorePixelRatio ||\n", - " this.context.mozBackingStorePixelRatio ||\n", - " this.context.msBackingStorePixelRatio ||\n", - " this.context.oBackingStorePixelRatio ||\n", - " this.context.backingStorePixelRatio ||\n", - " 1;\n", - "\n", - " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", - " 'canvas'\n", - " ));\n", - " rubberband_canvas.setAttribute(\n", - " 'style',\n", - " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", - " );\n", - "\n", - " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", - " if (this.ResizeObserver === undefined) {\n", - " if (window.ResizeObserver !== undefined) {\n", - " this.ResizeObserver = window.ResizeObserver;\n", - " } else {\n", - " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", - " this.ResizeObserver = obs.ResizeObserver;\n", - " }\n", - " }\n", - "\n", - " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", - " var nentries = entries.length;\n", - " for (var i = 0; i < nentries; i++) {\n", - " var entry = entries[i];\n", - " var width, height;\n", - " if (entry.contentBoxSize) {\n", - " if (entry.contentBoxSize instanceof Array) {\n", - " // Chrome 84 implements new version of spec.\n", - " width = entry.contentBoxSize[0].inlineSize;\n", - " height = entry.contentBoxSize[0].blockSize;\n", - " } else {\n", - " // Firefox implements old version of spec.\n", - " width = entry.contentBoxSize.inlineSize;\n", - " height = entry.contentBoxSize.blockSize;\n", - " }\n", - " } else {\n", - " // Chrome <84 implements even older version of spec.\n", - " width = entry.contentRect.width;\n", - " height = entry.contentRect.height;\n", - " }\n", - "\n", - " // Keep the size of the canvas and rubber band canvas in sync with\n", - " // the canvas container.\n", - " if (entry.devicePixelContentBoxSize) {\n", - " // Chrome 84 implements new version of spec.\n", - " canvas.setAttribute(\n", - " 'width',\n", - " entry.devicePixelContentBoxSize[0].inlineSize\n", - " );\n", - " canvas.setAttribute(\n", - " 'height',\n", - " entry.devicePixelContentBoxSize[0].blockSize\n", - " );\n", - " } else {\n", - " canvas.setAttribute('width', width * fig.ratio);\n", - " canvas.setAttribute('height', height * fig.ratio);\n", - " }\n", - " canvas.setAttribute(\n", - " 'style',\n", - " 'width: ' + width + 'px; height: ' + height + 'px;'\n", - " );\n", - "\n", - " rubberband_canvas.setAttribute('width', width);\n", - " rubberband_canvas.setAttribute('height', height);\n", - "\n", - " // And update the size in Python. We ignore the initial 0/0 size\n", - " // that occurs as the element is placed into the DOM, which should\n", - " // otherwise not happen due to the minimum size styling.\n", - " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", - " fig.request_resize(width, height);\n", - " }\n", - " }\n", - " });\n", - " this.resizeObserverInstance.observe(canvas_div);\n", - "\n", - " function on_mouse_event_closure(name) {\n", - " return function (event) {\n", - " return fig.mouse_event(event, name);\n", - " };\n", - " }\n", - "\n", - " rubberband_canvas.addEventListener(\n", - " 'mousedown',\n", - " on_mouse_event_closure('button_press')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseup',\n", - " on_mouse_event_closure('button_release')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'dblclick',\n", - " on_mouse_event_closure('dblclick')\n", - " );\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband_canvas.addEventListener(\n", - " 'mousemove',\n", - " on_mouse_event_closure('motion_notify')\n", - " );\n", - "\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseenter',\n", - " on_mouse_event_closure('figure_enter')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseleave',\n", - " on_mouse_event_closure('figure_leave')\n", - " );\n", - "\n", - " canvas_div.addEventListener('wheel', function (event) {\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " on_mouse_event_closure('scroll')(event);\n", - " });\n", - "\n", - " canvas_div.appendChild(canvas);\n", - " canvas_div.appendChild(rubberband_canvas);\n", - "\n", - " this.rubberband_context = rubberband_canvas.getContext('2d');\n", - " this.rubberband_context.strokeStyle = '#000000';\n", - "\n", - " this._resize_canvas = function (width, height, forward) {\n", - " if (forward) {\n", - " canvas_div.style.width = width + 'px';\n", - " canvas_div.style.height = height + 'px';\n", - " }\n", - " };\n", - "\n", - " // Disable right mouse context menu.\n", - " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", - " event.preventDefault();\n", - " return false;\n", - " });\n", - "\n", - " function set_focus() {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "};\n", - "\n", - "mpl.figure.prototype._init_toolbar = function () {\n", - " var fig = this;\n", - "\n", - " var toolbar = document.createElement('div');\n", - " toolbar.classList = 'mpl-toolbar';\n", - " this.root.appendChild(toolbar);\n", - "\n", - " function on_click_closure(name) {\n", - " return function (_event) {\n", - " return fig.toolbar_button_onclick(name);\n", - " };\n", - " }\n", - "\n", - " function on_mouseover_closure(tooltip) {\n", - " return function (event) {\n", - " if (!event.currentTarget.disabled) {\n", - " return fig.toolbar_button_onmouseover(tooltip);\n", - " }\n", - " };\n", - " }\n", - "\n", - " fig.buttons = {};\n", - " var buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'mpl-button-group';\n", - " for (var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " /* Instead of a spacer, we start a new button group. */\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - " buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'mpl-button-group';\n", - " continue;\n", - " }\n", - "\n", - " var button = (fig.buttons[name] = document.createElement('button'));\n", - " button.classList = 'mpl-widget';\n", - " button.setAttribute('role', 'button');\n", - " button.setAttribute('aria-disabled', 'false');\n", - " button.addEventListener('click', on_click_closure(method_name));\n", - " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", - "\n", - " var icon_img = document.createElement('img');\n", - " icon_img.src = '_images/' + image + '.png';\n", - " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", - " icon_img.alt = tooltip;\n", - " button.appendChild(icon_img);\n", - "\n", - " buttonGroup.appendChild(button);\n", - " }\n", - "\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - "\n", - " var fmt_picker = document.createElement('select');\n", - " fmt_picker.classList = 'mpl-widget';\n", - " toolbar.appendChild(fmt_picker);\n", - " this.format_dropdown = fmt_picker;\n", - "\n", - " for (var ind in mpl.extensions) {\n", - " var fmt = mpl.extensions[ind];\n", - " var option = document.createElement('option');\n", - " option.selected = fmt === mpl.default_extension;\n", - " option.innerHTML = fmt;\n", - " fmt_picker.appendChild(option);\n", - " }\n", - "\n", - " var status_bar = document.createElement('span');\n", - " status_bar.classList = 'mpl-message';\n", - " toolbar.appendChild(status_bar);\n", - " this.message = status_bar;\n", - "};\n", - "\n", - "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", - " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", - " // which will in turn request a refresh of the image.\n", - " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", - "};\n", - "\n", - "mpl.figure.prototype.send_message = function (type, properties) {\n", - " properties['type'] = type;\n", - " properties['figure_id'] = this.id;\n", - " this.ws.send(JSON.stringify(properties));\n", - "};\n", - "\n", - "mpl.figure.prototype.send_draw_message = function () {\n", - " if (!this.waiting) {\n", - " this.waiting = true;\n", - " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", - " var format_dropdown = fig.format_dropdown;\n", - " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", - " fig.ondownload(fig, format);\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", - " var size = msg['size'];\n", - " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", - " fig._resize_canvas(size[0], size[1], msg['forward']);\n", - " fig.send_message('refresh', {});\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", - " var x0 = msg['x0'] / fig.ratio;\n", - " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", - " var x1 = msg['x1'] / fig.ratio;\n", - " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", - " x0 = Math.floor(x0) + 0.5;\n", - " y0 = Math.floor(y0) + 0.5;\n", - " x1 = Math.floor(x1) + 0.5;\n", - " y1 = Math.floor(y1) + 0.5;\n", - " var min_x = Math.min(x0, x1);\n", - " var min_y = Math.min(y0, y1);\n", - " var width = Math.abs(x1 - x0);\n", - " var height = Math.abs(y1 - y0);\n", - "\n", - " fig.rubberband_context.clearRect(\n", - " 0,\n", - " 0,\n", - " fig.canvas.width / fig.ratio,\n", - " fig.canvas.height / fig.ratio\n", - " );\n", - "\n", - " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", - " // Updates the figure title.\n", - " fig.header.textContent = msg['label'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", - " var cursor = msg['cursor'];\n", - " switch (cursor) {\n", - " case 0:\n", - " cursor = 'pointer';\n", - " break;\n", - " case 1:\n", - " cursor = 'default';\n", - " break;\n", - " case 2:\n", - " cursor = 'crosshair';\n", - " break;\n", - " case 3:\n", - " cursor = 'move';\n", - " break;\n", - " }\n", - " fig.rubberband_canvas.style.cursor = cursor;\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_message = function (fig, msg) {\n", - " fig.message.textContent = msg['message'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", - " // Request the server to send over a new figure.\n", - " fig.send_draw_message();\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", - " fig.image_mode = msg['mode'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", - " for (var key in msg) {\n", - " if (!(key in fig.buttons)) {\n", - " continue;\n", - " }\n", - " fig.buttons[key].disabled = !msg[key];\n", - " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", - " if (msg['mode'] === 'PAN') {\n", - " fig.buttons['Pan'].classList.add('active');\n", - " fig.buttons['Zoom'].classList.remove('active');\n", - " } else if (msg['mode'] === 'ZOOM') {\n", - " fig.buttons['Pan'].classList.remove('active');\n", - " fig.buttons['Zoom'].classList.add('active');\n", - " } else {\n", - " fig.buttons['Pan'].classList.remove('active');\n", - " fig.buttons['Zoom'].classList.remove('active');\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.updated_canvas_event = function () {\n", - " // Called whenever the canvas gets updated.\n", - " this.send_message('ack', {});\n", - "};\n", - "\n", - "// A function to construct a web socket function for onmessage handling.\n", - "// Called in the figure constructor.\n", - "mpl.figure.prototype._make_on_message_function = function (fig) {\n", - " return function socket_on_message(evt) {\n", - " if (evt.data instanceof Blob) {\n", - " var img = evt.data;\n", - " if (img.type !== 'image/png') {\n", - " /* FIXME: We get \"Resource interpreted as Image but\n", - " * transferred with MIME type text/plain:\" errors on\n", - " * Chrome. But how to set the MIME type? It doesn't seem\n", - " * to be part of the websocket stream */\n", - " img.type = 'image/png';\n", - " }\n", - "\n", - " /* Free the memory for the previous frames */\n", - " if (fig.imageObj.src) {\n", - " (window.URL || window.webkitURL).revokeObjectURL(\n", - " fig.imageObj.src\n", - " );\n", - " }\n", - "\n", - " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", - " img\n", - " );\n", - " fig.updated_canvas_event();\n", - " fig.waiting = false;\n", - " return;\n", - " } else if (\n", - " typeof evt.data === 'string' &&\n", - " evt.data.slice(0, 21) === 'data:image/png;base64'\n", - " ) {\n", - " fig.imageObj.src = evt.data;\n", - " fig.updated_canvas_event();\n", - " fig.waiting = false;\n", - " return;\n", - " }\n", - "\n", - " var msg = JSON.parse(evt.data);\n", - " var msg_type = msg['type'];\n", - "\n", - " // Call the \"handle_{type}\" callback, which takes\n", - " // the figure and JSON message as its only arguments.\n", - " try {\n", - " var callback = fig['handle_' + msg_type];\n", - " } catch (e) {\n", - " console.log(\n", - " \"No handler for the '\" + msg_type + \"' message type: \",\n", - " msg\n", - " );\n", - " return;\n", - " }\n", - "\n", - " if (callback) {\n", - " try {\n", - " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", - " callback(fig, msg);\n", - " } catch (e) {\n", - " console.log(\n", - " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", - " e,\n", - " e.stack,\n", - " msg\n", - " );\n", - " }\n", - " }\n", - " };\n", - "};\n", - "\n", - "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", - "mpl.findpos = function (e) {\n", - " //this section is from http://www.quirksmode.org/js/events_properties.html\n", - " var targ;\n", - " if (!e) {\n", - " e = window.event;\n", - " }\n", - " if (e.target) {\n", - " targ = e.target;\n", - " } else if (e.srcElement) {\n", - " targ = e.srcElement;\n", - " }\n", - " if (targ.nodeType === 3) {\n", - " // defeat Safari bug\n", - " targ = targ.parentNode;\n", - " }\n", - "\n", - " // pageX,Y are the mouse positions relative to the document\n", - " var boundingRect = targ.getBoundingClientRect();\n", - " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", - " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", - "\n", - " return { x: x, y: y };\n", - "};\n", - "\n", - "/*\n", - " * return a copy of an object with only non-object keys\n", - " * we need this to avoid circular references\n", - " * http://stackoverflow.com/a/24161582/3208463\n", - " */\n", - "function simpleKeys(original) {\n", - " return Object.keys(original).reduce(function (obj, key) {\n", - " if (typeof original[key] !== 'object') {\n", - " obj[key] = original[key];\n", - " }\n", - " return obj;\n", - " }, {});\n", - "}\n", - "\n", - "mpl.figure.prototype.mouse_event = function (event, name) {\n", - " var canvas_pos = mpl.findpos(event);\n", - "\n", - " if (name === 'button_press') {\n", - " this.canvas.focus();\n", - " this.canvas_div.focus();\n", - " }\n", - "\n", - " var x = canvas_pos.x * this.ratio;\n", - " var y = canvas_pos.y * this.ratio;\n", - "\n", - " this.send_message(name, {\n", - " x: x,\n", - " y: y,\n", - " button: event.button,\n", - " step: event.step,\n", - " guiEvent: simpleKeys(event),\n", - " });\n", - "\n", - " /* This prevents the web browser from automatically changing to\n", - " * the text insertion cursor when the button is pressed. We want\n", - " * to control all of the cursor setting manually through the\n", - " * 'cursor' event from matplotlib */\n", - " event.preventDefault();\n", - " return false;\n", - "};\n", - "\n", - "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", - " // Handle any extra behaviour associated with a key event\n", - "};\n", - "\n", - "mpl.figure.prototype.key_event = function (event, name) {\n", - " // Prevent repeat events\n", - " if (name === 'key_press') {\n", - " if (event.key === this._key) {\n", - " return;\n", - " } else {\n", - " this._key = event.key;\n", - " }\n", - " }\n", - " if (name === 'key_release') {\n", - " this._key = null;\n", - " }\n", - "\n", - " var value = '';\n", - " if (event.ctrlKey && event.key !== 'Control') {\n", - " value += 'ctrl+';\n", - " }\n", - " else if (event.altKey && event.key !== 'Alt') {\n", - " value += 'alt+';\n", - " }\n", - " else if (event.shiftKey && event.key !== 'Shift') {\n", - " value += 'shift+';\n", - " }\n", - "\n", - " value += 'k' + event.key;\n", - "\n", - " this._key_event_extra(event, name);\n", - "\n", - " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", - " return false;\n", - "};\n", - "\n", - "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", - " if (name === 'download') {\n", - " this.handle_save(this, null);\n", - " } else {\n", - " this.send_message('toolbar_button', { name: name });\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", - " this.message.textContent = tooltip;\n", - "};\n", - "\n", - "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", - "// prettier-ignore\n", - "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", - "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", - "\n", - "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", - "\n", - "mpl.default_extension = \"png\";/* global mpl */\n", - "\n", - "var comm_websocket_adapter = function (comm) {\n", - " // Create a \"websocket\"-like object which calls the given IPython comm\n", - " // object with the appropriate methods. Currently this is a non binary\n", - " // socket, so there is still some room for performance tuning.\n", - " var ws = {};\n", - "\n", - " ws.binaryType = comm.kernel.ws.binaryType;\n", - " ws.readyState = comm.kernel.ws.readyState;\n", - " function updateReadyState(_event) {\n", - " if (comm.kernel.ws) {\n", - " ws.readyState = comm.kernel.ws.readyState;\n", - " } else {\n", - " ws.readyState = 3; // Closed state.\n", - " }\n", - " }\n", - " comm.kernel.ws.addEventListener('open', updateReadyState);\n", - " comm.kernel.ws.addEventListener('close', updateReadyState);\n", - " comm.kernel.ws.addEventListener('error', updateReadyState);\n", - "\n", - " ws.close = function () {\n", - " comm.close();\n", - " };\n", - " ws.send = function (m) {\n", - " //console.log('sending', m);\n", - " comm.send(m);\n", - " };\n", - " // Register the callback with on_msg.\n", - " comm.on_msg(function (msg) {\n", - " //console.log('receiving', msg['content']['data'], msg);\n", - " var data = msg['content']['data'];\n", - " if (data['blob'] !== undefined) {\n", - " data = {\n", - " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", - " };\n", - " }\n", - " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", - " ws.onmessage(data);\n", - " });\n", - " return ws;\n", - "};\n", - "\n", - "mpl.mpl_figure_comm = function (comm, msg) {\n", - " // This is the function which gets called when the mpl process\n", - " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", - "\n", - " var id = msg.content.data.id;\n", - " // Get hold of the div created by the display call when the Comm\n", - " // socket was opened in Python.\n", - " var element = document.getElementById(id);\n", - " var ws_proxy = comm_websocket_adapter(comm);\n", - "\n", - " function ondownload(figure, _format) {\n", - " window.open(figure.canvas.toDataURL());\n", - " }\n", - "\n", - " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", - "\n", - " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", - " // web socket which is closed, not our websocket->open comm proxy.\n", - " ws_proxy.onopen();\n", - "\n", - " fig.parent_element = element;\n", - " fig.cell_info = mpl.find_output_cell(\"
\");\n", - " if (!fig.cell_info) {\n", - " console.error('Failed to find cell for figure', id, fig);\n", - " return;\n", - " }\n", - " fig.cell_info[0].output_area.element.on(\n", - " 'cleared',\n", - " { fig: fig },\n", - " fig._remove_fig_handler\n", - " );\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_close = function (fig, msg) {\n", - " var width = fig.canvas.width / fig.ratio;\n", - " fig.cell_info[0].output_area.element.off(\n", - " 'cleared',\n", - " fig._remove_fig_handler\n", - " );\n", - " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", - "\n", - " // Update the output cell to use the data from the current canvas.\n", - " fig.push_to_output();\n", - " var dataURL = fig.canvas.toDataURL();\n", - " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", - " // the notebook keyboard shortcuts fail.\n", - " IPython.keyboard_manager.enable();\n", - " fig.parent_element.innerHTML =\n", - " '';\n", - " fig.close_ws(fig, msg);\n", - "};\n", - "\n", - "mpl.figure.prototype.close_ws = function (fig, msg) {\n", - " fig.send_message('closing', msg);\n", - " // fig.ws.close()\n", - "};\n", - "\n", - "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", - " // Turn the data on the canvas into data in the output cell.\n", - " var width = this.canvas.width / this.ratio;\n", - " var dataURL = this.canvas.toDataURL();\n", - " this.cell_info[1]['text/html'] =\n", - " '';\n", - "};\n", - "\n", - "mpl.figure.prototype.updated_canvas_event = function () {\n", - " // Tell IPython that the notebook contents must change.\n", - " IPython.notebook.set_dirty(true);\n", - " this.send_message('ack', {});\n", - " var fig = this;\n", - " // Wait a second, then push the new image to the DOM so\n", - " // that it is saved nicely (might be nice to debounce this).\n", - " setTimeout(function () {\n", - " fig.push_to_output();\n", - " }, 1000);\n", - "};\n", - "\n", - "mpl.figure.prototype._init_toolbar = function () {\n", - " var fig = this;\n", - "\n", - " var toolbar = document.createElement('div');\n", - " toolbar.classList = 'btn-toolbar';\n", - " this.root.appendChild(toolbar);\n", - "\n", - " function on_click_closure(name) {\n", - " return function (_event) {\n", - " return fig.toolbar_button_onclick(name);\n", - " };\n", - " }\n", - "\n", - " function on_mouseover_closure(tooltip) {\n", - " return function (event) {\n", - " if (!event.currentTarget.disabled) {\n", - " return fig.toolbar_button_onmouseover(tooltip);\n", - " }\n", - " };\n", - " }\n", - "\n", - " fig.buttons = {};\n", - " var buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'btn-group';\n", - " var button;\n", - " for (var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " /* Instead of a spacer, we start a new button group. */\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - " buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'btn-group';\n", - " continue;\n", - " }\n", - "\n", - " button = fig.buttons[name] = document.createElement('button');\n", - " button.classList = 'btn btn-default';\n", - " button.href = '#';\n", - " button.title = name;\n", - " button.innerHTML = '';\n", - " button.addEventListener('click', on_click_closure(method_name));\n", - " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", - " buttonGroup.appendChild(button);\n", - " }\n", - "\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - "\n", - " // Add the status bar.\n", - " var status_bar = document.createElement('span');\n", - " status_bar.classList = 'mpl-message pull-right';\n", - " toolbar.appendChild(status_bar);\n", - " this.message = status_bar;\n", - "\n", - " // Add the close button to the window.\n", - " var buttongrp = document.createElement('div');\n", - " buttongrp.classList = 'btn-group inline pull-right';\n", - " button = document.createElement('button');\n", - " button.classList = 'btn btn-mini btn-primary';\n", - " button.href = '#';\n", - " button.title = 'Stop Interaction';\n", - " button.innerHTML = '';\n", - " button.addEventListener('click', function (_evt) {\n", - " fig.handle_close(fig, {});\n", - " });\n", - " button.addEventListener(\n", - " 'mouseover',\n", - " on_mouseover_closure('Stop Interaction')\n", - " );\n", - " buttongrp.appendChild(button);\n", - " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", - " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", - "};\n", - "\n", - "mpl.figure.prototype._remove_fig_handler = function (event) {\n", - " var fig = event.data.fig;\n", - " if (event.target !== this) {\n", - " // Ignore bubbled events from children.\n", - " return;\n", - " }\n", - " fig.close_ws(fig, {});\n", - "};\n", - "\n", - "mpl.figure.prototype._root_extra_style = function (el) {\n", - " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", - "};\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function (el) {\n", - " // this is important to make the div 'focusable\n", - " el.setAttribute('tabindex', 0);\n", - " // reach out to IPython and tell the keyboard manager to turn it's self\n", - " // off when our div gets focus\n", - "\n", - " // location in version 3\n", - " if (IPython.notebook.keyboard_manager) {\n", - " IPython.notebook.keyboard_manager.register_events(el);\n", - " } else {\n", - " // location in version 2\n", - " IPython.keyboard_manager.register_events(el);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", - " var manager = IPython.notebook.keyboard_manager;\n", - " if (!manager) {\n", - " manager = IPython.keyboard_manager;\n", - " }\n", - "\n", - " // Check for shift+enter\n", - " if (event.shiftKey && event.which === 13) {\n", - " this.canvas_div.blur();\n", - " // select the cell after this one\n", - " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", - " IPython.notebook.select(index + 1);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", - " fig.ondownload(fig, null);\n", - "};\n", - "\n", - "mpl.find_output_cell = function (html_output) {\n", - " // Return the cell and output element which can be found *uniquely* in the notebook.\n", - " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", - " // IPython event is triggered only after the cells have been serialised, which for\n", - " // our purposes (turning an active figure into a static one), is too late.\n", - " var cells = IPython.notebook.get_cells();\n", - " var ncells = cells.length;\n", - " for (var i = 0; i < ncells; i++) {\n", - " var cell = cells[i];\n", - " if (cell.cell_type === 'code') {\n", - " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", - " var data = cell.output_area.outputs[j];\n", - " if (data.data) {\n", - " // IPython >= 3 moved mimebundle to data attribute of output\n", - " data = data.data;\n", - " }\n", - " if (data['text/html'] === html_output) {\n", - " return [cell, data, j];\n", - " }\n", - " }\n", - " }\n", - " }\n", - "};\n", - "\n", - "// Register the function which deals with the matplotlib target/channel.\n", - "// The kernel may be null if the page has been refreshed.\n", - "if (IPython.notebook.kernel !== null) {\n", - " IPython.notebook.kernel.comm_manager.register_target(\n", - " 'matplotlib',\n", - " mpl.mpl_figure_comm\n", - " );\n", - "}\n" - ], + "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.get_websocket_type = function () {\n if (typeof WebSocket !== 'undefined') {\n return WebSocket;\n } else if (typeof MozWebSocket !== 'undefined') {\n return MozWebSocket;\n } else {\n alert(\n 'Your browser does not have WebSocket support. ' +\n 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n 'Firefox 4 and 5 are also supported but you ' +\n 'have to enable WebSockets in about:config.'\n );\n }\n};\n\nmpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n this.id = figure_id;\n\n this.ws = websocket;\n\n this.supports_binary = this.ws.binaryType !== undefined;\n\n if (!this.supports_binary) {\n var warnings = document.getElementById('mpl-warnings');\n if (warnings) {\n warnings.style.display = 'block';\n warnings.textContent =\n 'This browser does not support binary websocket messages. ' +\n 'Performance may be slow.';\n }\n }\n\n this.imageObj = new Image();\n\n this.context = undefined;\n this.message = undefined;\n this.canvas = undefined;\n this.rubberband_canvas = undefined;\n this.rubberband_context = undefined;\n this.format_dropdown = undefined;\n\n this.image_mode = 'full';\n\n this.root = document.createElement('div');\n this.root.setAttribute('style', 'display: inline-block');\n this._root_extra_style(this.root);\n\n parent_element.appendChild(this.root);\n\n this._init_header(this);\n this._init_canvas(this);\n this._init_toolbar(this);\n\n var fig = this;\n\n this.waiting = false;\n\n this.ws.onopen = function () {\n fig.send_message('supports_binary', { value: fig.supports_binary });\n fig.send_message('send_image_mode', {});\n if (fig.ratio !== 1) {\n fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n }\n fig.send_message('refresh', {});\n };\n\n this.imageObj.onload = function () {\n if (fig.image_mode === 'full') {\n // Full images could contain transparency (where diff images\n // almost always do), so we need to clear the canvas so that\n // there is no ghosting.\n fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n }\n fig.context.drawImage(fig.imageObj, 0, 0);\n };\n\n this.imageObj.onunload = function () {\n fig.ws.close();\n };\n\n this.ws.onmessage = this._make_on_message_function(this);\n\n this.ondownload = ondownload;\n};\n\nmpl.figure.prototype._init_header = function () {\n var titlebar = document.createElement('div');\n titlebar.classList =\n 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n var titletext = document.createElement('div');\n titletext.classList = 'ui-dialog-title';\n titletext.setAttribute(\n 'style',\n 'width: 100%; text-align: center; padding: 3px;'\n );\n titlebar.appendChild(titletext);\n this.root.appendChild(titlebar);\n this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n var fig = this;\n\n var canvas_div = (this.canvas_div = document.createElement('div'));\n canvas_div.setAttribute(\n 'style',\n 'border: 1px solid #ddd;' +\n 'box-sizing: content-box;' +\n 'clear: both;' +\n 'min-height: 1px;' +\n 'min-width: 1px;' +\n 'outline: 0;' +\n 'overflow: hidden;' +\n 'position: relative;' +\n 'resize: both;'\n );\n\n function on_keyboard_event_closure(name) {\n return function (event) {\n return fig.key_event(event, name);\n };\n }\n\n canvas_div.addEventListener(\n 'keydown',\n on_keyboard_event_closure('key_press')\n );\n canvas_div.addEventListener(\n 'keyup',\n on_keyboard_event_closure('key_release')\n );\n\n this._canvas_extra_style(canvas_div);\n this.root.appendChild(canvas_div);\n\n var canvas = (this.canvas = document.createElement('canvas'));\n canvas.classList.add('mpl-canvas');\n canvas.setAttribute('style', 'box-sizing: content-box;');\n\n this.context = canvas.getContext('2d');\n\n var backingStore =\n this.context.backingStorePixelRatio ||\n this.context.webkitBackingStorePixelRatio ||\n this.context.mozBackingStorePixelRatio ||\n this.context.msBackingStorePixelRatio ||\n this.context.oBackingStorePixelRatio ||\n this.context.backingStorePixelRatio ||\n 1;\n\n this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n 'canvas'\n ));\n rubberband_canvas.setAttribute(\n 'style',\n 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n );\n\n // Apply a ponyfill if ResizeObserver is not implemented by browser.\n if (this.ResizeObserver === undefined) {\n if (window.ResizeObserver !== undefined) {\n this.ResizeObserver = window.ResizeObserver;\n } else {\n var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n this.ResizeObserver = obs.ResizeObserver;\n }\n }\n\n this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n var nentries = entries.length;\n for (var i = 0; i < nentries; i++) {\n var entry = entries[i];\n var width, height;\n if (entry.contentBoxSize) {\n if (entry.contentBoxSize instanceof Array) {\n // Chrome 84 implements new version of spec.\n width = entry.contentBoxSize[0].inlineSize;\n height = entry.contentBoxSize[0].blockSize;\n } else {\n // Firefox implements old version of spec.\n width = entry.contentBoxSize.inlineSize;\n height = entry.contentBoxSize.blockSize;\n }\n } else {\n // Chrome <84 implements even older version of spec.\n width = entry.contentRect.width;\n height = entry.contentRect.height;\n }\n\n // Keep the size of the canvas and rubber band canvas in sync with\n // the canvas container.\n if (entry.devicePixelContentBoxSize) {\n // Chrome 84 implements new version of spec.\n canvas.setAttribute(\n 'width',\n entry.devicePixelContentBoxSize[0].inlineSize\n );\n canvas.setAttribute(\n 'height',\n entry.devicePixelContentBoxSize[0].blockSize\n );\n } else {\n canvas.setAttribute('width', width * fig.ratio);\n canvas.setAttribute('height', height * fig.ratio);\n }\n canvas.setAttribute(\n 'style',\n 'width: ' + width + 'px; height: ' + height + 'px;'\n );\n\n rubberband_canvas.setAttribute('width', width);\n rubberband_canvas.setAttribute('height', height);\n\n // And update the size in Python. We ignore the initial 0/0 size\n // that occurs as the element is placed into the DOM, which should\n // otherwise not happen due to the minimum size styling.\n if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n fig.request_resize(width, height);\n }\n }\n });\n this.resizeObserverInstance.observe(canvas_div);\n\n function on_mouse_event_closure(name) {\n return function (event) {\n return fig.mouse_event(event, name);\n };\n }\n\n rubberband_canvas.addEventListener(\n 'mousedown',\n on_mouse_event_closure('button_press')\n );\n rubberband_canvas.addEventListener(\n 'mouseup',\n on_mouse_event_closure('button_release')\n );\n rubberband_canvas.addEventListener(\n 'dblclick',\n on_mouse_event_closure('dblclick')\n );\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband_canvas.addEventListener(\n 'mousemove',\n on_mouse_event_closure('motion_notify')\n );\n\n rubberband_canvas.addEventListener(\n 'mouseenter',\n on_mouse_event_closure('figure_enter')\n );\n rubberband_canvas.addEventListener(\n 'mouseleave',\n on_mouse_event_closure('figure_leave')\n );\n\n canvas_div.addEventListener('wheel', function (event) {\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n on_mouse_event_closure('scroll')(event);\n });\n\n canvas_div.appendChild(canvas);\n canvas_div.appendChild(rubberband_canvas);\n\n this.rubberband_context = rubberband_canvas.getContext('2d');\n this.rubberband_context.strokeStyle = '#000000';\n\n this._resize_canvas = function (width, height, forward) {\n if (forward) {\n canvas_div.style.width = width + 'px';\n canvas_div.style.height = height + 'px';\n }\n };\n\n // Disable right mouse context menu.\n this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n event.preventDefault();\n return false;\n });\n\n function set_focus() {\n canvas.focus();\n canvas_div.focus();\n }\n\n window.setTimeout(set_focus, 100);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'mpl-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n for (var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n continue;\n }\n\n var button = (fig.buttons[name] = document.createElement('button'));\n button.classList = 'mpl-widget';\n button.setAttribute('role', 'button');\n button.setAttribute('aria-disabled', 'false');\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n var icon_img = document.createElement('img');\n icon_img.src = '_images/' + image + '.png';\n icon_img.srcset = '_images/' + image + '_large.png 2x';\n icon_img.alt = tooltip;\n button.appendChild(icon_img);\n\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n var fmt_picker = document.createElement('select');\n fmt_picker.classList = 'mpl-widget';\n toolbar.appendChild(fmt_picker);\n this.format_dropdown = fmt_picker;\n\n for (var ind in mpl.extensions) {\n var fmt = mpl.extensions[ind];\n var option = document.createElement('option');\n option.selected = fmt === mpl.default_extension;\n option.innerHTML = fmt;\n fmt_picker.appendChild(option);\n }\n\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n};\n\nmpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n // which will in turn request a refresh of the image.\n this.send_message('resize', { width: x_pixels, height: y_pixels });\n};\n\nmpl.figure.prototype.send_message = function (type, properties) {\n properties['type'] = type;\n properties['figure_id'] = this.id;\n this.ws.send(JSON.stringify(properties));\n};\n\nmpl.figure.prototype.send_draw_message = function () {\n if (!this.waiting) {\n this.waiting = true;\n this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n var format_dropdown = fig.format_dropdown;\n var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n fig.ondownload(fig, format);\n};\n\nmpl.figure.prototype.handle_resize = function (fig, msg) {\n var size = msg['size'];\n if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n fig._resize_canvas(size[0], size[1], msg['forward']);\n fig.send_message('refresh', {});\n }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n var x0 = msg['x0'] / fig.ratio;\n var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n var x1 = msg['x1'] / fig.ratio;\n var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n x0 = Math.floor(x0) + 0.5;\n y0 = Math.floor(y0) + 0.5;\n x1 = Math.floor(x1) + 0.5;\n y1 = Math.floor(y1) + 0.5;\n var min_x = Math.min(x0, x1);\n var min_y = Math.min(y0, y1);\n var width = Math.abs(x1 - x0);\n var height = Math.abs(y1 - y0);\n\n fig.rubberband_context.clearRect(\n 0,\n 0,\n fig.canvas.width / fig.ratio,\n fig.canvas.height / fig.ratio\n );\n\n fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n var cursor = msg['cursor'];\n switch (cursor) {\n case 0:\n cursor = 'pointer';\n break;\n case 1:\n cursor = 'default';\n break;\n case 2:\n cursor = 'crosshair';\n break;\n case 3:\n cursor = 'move';\n break;\n }\n fig.rubberband_canvas.style.cursor = cursor;\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n for (var key in msg) {\n if (!(key in fig.buttons)) {\n continue;\n }\n fig.buttons[key].disabled = !msg[key];\n fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n if (msg['mode'] === 'PAN') {\n fig.buttons['Pan'].classList.add('active');\n fig.buttons['Zoom'].classList.remove('active');\n } else if (msg['mode'] === 'ZOOM') {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.add('active');\n } else {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.remove('active');\n }\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n // Called whenever the canvas gets updated.\n this.send_message('ack', {});\n};\n\n// A function to construct a web socket function for onmessage handling.\n// Called in the figure constructor.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n return function socket_on_message(evt) {\n if (evt.data instanceof Blob) {\n var img = evt.data;\n if (img.type !== 'image/png') {\n /* FIXME: We get \"Resource interpreted as Image but\n * transferred with MIME type text/plain:\" errors on\n * Chrome. But how to set the MIME type? It doesn't seem\n * to be part of the websocket stream */\n img.type = 'image/png';\n }\n\n /* Free the memory for the previous frames */\n if (fig.imageObj.src) {\n (window.URL || window.webkitURL).revokeObjectURL(\n fig.imageObj.src\n );\n }\n\n fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n img\n );\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n } else if (\n typeof evt.data === 'string' &&\n evt.data.slice(0, 21) === 'data:image/png;base64'\n ) {\n fig.imageObj.src = evt.data;\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n }\n\n var msg = JSON.parse(evt.data);\n var msg_type = msg['type'];\n\n // Call the \"handle_{type}\" callback, which takes\n // the figure and JSON message as its only arguments.\n try {\n var callback = fig['handle_' + msg_type];\n } catch (e) {\n console.log(\n \"No handler for the '\" + msg_type + \"' message type: \",\n msg\n );\n return;\n }\n\n if (callback) {\n try {\n // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n callback(fig, msg);\n } catch (e) {\n console.log(\n \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n e,\n e.stack,\n msg\n );\n }\n }\n };\n};\n\n// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.findpos = function (e) {\n //this section is from http://www.quirksmode.org/js/events_properties.html\n var targ;\n if (!e) {\n e = window.event;\n }\n if (e.target) {\n targ = e.target;\n } else if (e.srcElement) {\n targ = e.srcElement;\n }\n if (targ.nodeType === 3) {\n // defeat Safari bug\n targ = targ.parentNode;\n }\n\n // pageX,Y are the mouse positions relative to the document\n var boundingRect = targ.getBoundingClientRect();\n var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n\n return { x: x, y: y };\n};\n\n/*\n * return a copy of an object with only non-object keys\n * we need this to avoid circular references\n * http://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n return Object.keys(original).reduce(function (obj, key) {\n if (typeof original[key] !== 'object') {\n obj[key] = original[key];\n }\n return obj;\n }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n var canvas_pos = mpl.findpos(event);\n\n if (name === 'button_press') {\n this.canvas.focus();\n this.canvas_div.focus();\n }\n\n var x = canvas_pos.x * this.ratio;\n var y = canvas_pos.y * this.ratio;\n\n this.send_message(name, {\n x: x,\n y: y,\n button: event.button,\n step: event.step,\n guiEvent: simpleKeys(event),\n });\n\n /* This prevents the web browser from automatically changing to\n * the text insertion cursor when the button is pressed. We want\n * to control all of the cursor setting manually through the\n * 'cursor' event from matplotlib */\n event.preventDefault();\n return false;\n};\n\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n // Prevent repeat events\n if (name === 'key_press') {\n if (event.key === this._key) {\n return;\n } else {\n this._key = event.key;\n }\n }\n if (name === 'key_release') {\n this._key = null;\n }\n\n var value = '';\n if (event.ctrlKey && event.key !== 'Control') {\n value += 'ctrl+';\n }\n else if (event.altKey && event.key !== 'Alt') {\n value += 'alt+';\n }\n else if (event.shiftKey && event.key !== 'Shift') {\n value += 'shift+';\n }\n\n value += 'k' + event.key;\n\n this._key_event_extra(event, name);\n\n this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n return false;\n};\n\nmpl.figure.prototype.toolbar_button_onclick = function (name) {\n if (name === 'download') {\n this.handle_save(this, null);\n } else {\n this.send_message('toolbar_button', { name: name });\n }\n};\n\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar comm_websocket_adapter = function (comm) {\n // Create a \"websocket\"-like object which calls the given IPython comm\n // object with the appropriate methods. Currently this is a non binary\n // socket, so there is still some room for performance tuning.\n var ws = {};\n\n ws.binaryType = comm.kernel.ws.binaryType;\n ws.readyState = comm.kernel.ws.readyState;\n function updateReadyState(_event) {\n if (comm.kernel.ws) {\n ws.readyState = comm.kernel.ws.readyState;\n } else {\n ws.readyState = 3; // Closed state.\n }\n }\n comm.kernel.ws.addEventListener('open', updateReadyState);\n comm.kernel.ws.addEventListener('close', updateReadyState);\n comm.kernel.ws.addEventListener('error', updateReadyState);\n\n ws.close = function () {\n comm.close();\n };\n ws.send = function (m) {\n //console.log('sending', m);\n comm.send(m);\n };\n // Register the callback with on_msg.\n comm.on_msg(function (msg) {\n //console.log('receiving', msg['content']['data'], msg);\n var data = msg['content']['data'];\n if (data['blob'] !== undefined) {\n data = {\n data: new Blob(msg['buffers'], { type: data['blob'] }),\n };\n }\n // Pass the mpl event to the overridden (by mpl) onmessage function.\n ws.onmessage(data);\n });\n return ws;\n};\n\nmpl.mpl_figure_comm = function (comm, msg) {\n // This is the function which gets called when the mpl process\n // starts-up an IPython Comm through the \"matplotlib\" channel.\n\n var id = msg.content.data.id;\n // Get hold of the div created by the display call when the Comm\n // socket was opened in Python.\n var element = document.getElementById(id);\n var ws_proxy = comm_websocket_adapter(comm);\n\n function ondownload(figure, _format) {\n window.open(figure.canvas.toDataURL());\n }\n\n var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n\n // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n // web socket which is closed, not our websocket->open comm proxy.\n ws_proxy.onopen();\n\n fig.parent_element = element;\n fig.cell_info = mpl.find_output_cell(\"
\");\n if (!fig.cell_info) {\n console.error('Failed to find cell for figure', id, fig);\n return;\n }\n fig.cell_info[0].output_area.element.on(\n 'cleared',\n { fig: fig },\n fig._remove_fig_handler\n );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n var width = fig.canvas.width / fig.ratio;\n fig.cell_info[0].output_area.element.off(\n 'cleared',\n fig._remove_fig_handler\n );\n fig.resizeObserverInstance.unobserve(fig.canvas_div);\n\n // Update the output cell to use the data from the current canvas.\n fig.push_to_output();\n var dataURL = fig.canvas.toDataURL();\n // Re-enable the keyboard manager in IPython - without this line, in FF,\n // the notebook keyboard shortcuts fail.\n IPython.keyboard_manager.enable();\n fig.parent_element.innerHTML =\n '';\n fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n fig.send_message('closing', msg);\n // fig.ws.close()\n};\n\nmpl.figure.prototype.push_to_output = function (_remove_interactive) {\n // Turn the data on the canvas into data in the output cell.\n var width = this.canvas.width / this.ratio;\n var dataURL = this.canvas.toDataURL();\n this.cell_info[1]['text/html'] =\n '';\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n // Tell IPython that the notebook contents must change.\n IPython.notebook.set_dirty(true);\n this.send_message('ack', {});\n var fig = this;\n // Wait a second, then push the new image to the DOM so\n // that it is saved nicely (might be nice to debounce this).\n setTimeout(function () {\n fig.push_to_output();\n }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'btn-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n var button;\n for (var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n continue;\n }\n\n button = fig.buttons[name] = document.createElement('button');\n button.classList = 'btn btn-default';\n button.href = '#';\n button.title = name;\n button.innerHTML = '';\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n // Add the status bar.\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message pull-right';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n\n // Add the close button to the window.\n var buttongrp = document.createElement('div');\n buttongrp.classList = 'btn-group inline pull-right';\n button = document.createElement('button');\n button.classList = 'btn btn-mini btn-primary';\n button.href = '#';\n button.title = 'Stop Interaction';\n button.innerHTML = '';\n button.addEventListener('click', function (_evt) {\n fig.handle_close(fig, {});\n });\n button.addEventListener(\n 'mouseover',\n on_mouseover_closure('Stop Interaction')\n );\n buttongrp.appendChild(button);\n var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n var fig = event.data.fig;\n if (event.target !== this) {\n // Ignore bubbled events from children.\n return;\n }\n fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n // this is important to make the div 'focusable\n el.setAttribute('tabindex', 0);\n // reach out to IPython and tell the keyboard manager to turn it's self\n // off when our div gets focus\n\n // location in version 3\n if (IPython.notebook.keyboard_manager) {\n IPython.notebook.keyboard_manager.register_events(el);\n } else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n var manager = IPython.notebook.keyboard_manager;\n if (!manager) {\n manager = IPython.keyboard_manager;\n }\n\n // Check for shift+enter\n if (event.shiftKey && event.which === 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n fig.ondownload(fig, null);\n};\n\nmpl.find_output_cell = function (html_output) {\n // Return the cell and output element which can be found *uniquely* in the notebook.\n // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n // IPython event is triggered only after the cells have been serialised, which for\n // our purposes (turning an active figure into a static one), is too late.\n var cells = IPython.notebook.get_cells();\n var ncells = cells.length;\n for (var i = 0; i < ncells; i++) {\n var cell = cells[i];\n if (cell.cell_type === 'code') {\n for (var j = 0; j < cell.output_area.outputs.length; j++) {\n var data = cell.output_area.outputs[j];\n if (data.data) {\n // IPython >= 3 moved mimebundle to data attribute of output\n data = data.data;\n }\n if (data['text/html'] === html_output) {\n return [cell, data, j];\n }\n }\n }\n }\n};\n\n// Register the function which deals with the matplotlib target/channel.\n// The kernel may be null if the page has been refreshed.\nif (IPython.notebook.kernel !== null) {\n IPython.notebook.kernel.comm_manager.register_target(\n 'matplotlib',\n mpl.mpl_figure_comm\n );\n}\n", "text/plain": [ "" ] From fc9f2d48fd6014633b340f079f0124846c782f04 Mon Sep 17 00:00:00 2001 From: Arne Date: Wed, 26 Jan 2022 19:59:16 +0100 Subject: [PATCH 28/79] Bugfixes --- .../pmsm_mpc_dq_current_control.ipynb | 775 ++++- ...ble_baselines3_dqn_disc_pmsm_example.ipynb | 2492 +++++++++++++++-- 2 files changed, 2993 insertions(+), 274 deletions(-) diff --git a/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb b/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb index dbbfff9b..87285b98 100644 --- a/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb +++ b/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb @@ -313,7 +313,774 @@ "outputs": [ { "data": { - "application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.get_websocket_type = function() {\n if (typeof(WebSocket) !== 'undefined') {\n return WebSocket;\n } else if (typeof(MozWebSocket) !== 'undefined') {\n return MozWebSocket;\n } else {\n alert('Your browser does not have WebSocket support. ' +\n 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n 'Firefox 4 and 5 are also supported but you ' +\n 'have to enable WebSockets in about:config.');\n };\n}\n\nmpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n this.id = figure_id;\n\n this.ws = websocket;\n\n this.supports_binary = (this.ws.binaryType != undefined);\n\n if (!this.supports_binary) {\n var warnings = document.getElementById(\"mpl-warnings\");\n if (warnings) {\n warnings.style.display = 'block';\n warnings.textContent = (\n \"This browser does not support binary websocket messages. \" +\n \"Performance may be slow.\");\n }\n }\n\n this.imageObj = new Image();\n\n this.context = undefined;\n this.message = undefined;\n this.canvas = undefined;\n this.rubberband_canvas = undefined;\n this.rubberband_context = undefined;\n this.format_dropdown = undefined;\n\n this.image_mode = 'full';\n\n this.root = $('
');\n this._root_extra_style(this.root)\n this.root.attr('style', 'display: inline-block');\n\n $(parent_element).append(this.root);\n\n this._init_header(this);\n this._init_canvas(this);\n this._init_toolbar(this);\n\n var fig = this;\n\n this.waiting = false;\n\n this.ws.onopen = function () {\n fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n fig.send_message(\"send_image_mode\", {});\n if (mpl.ratio != 1) {\n fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n }\n fig.send_message(\"refresh\", {});\n }\n\n this.imageObj.onload = function() {\n if (fig.image_mode == 'full') {\n // Full images could contain transparency (where diff images\n // almost always do), so we need to clear the canvas so that\n // there is no ghosting.\n fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n }\n fig.context.drawImage(fig.imageObj, 0, 0);\n };\n\n this.imageObj.onunload = function() {\n fig.ws.close();\n }\n\n this.ws.onmessage = this._make_on_message_function(this);\n\n this.ondownload = ondownload;\n}\n\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_div = $('
');\n\n canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n\n function canvas_keyboard_event(event) {\n return fig.key_event(event, event['data']);\n }\n\n canvas_div.keydown('key_press', canvas_keyboard_event);\n canvas_div.keyup('key_release', canvas_keyboard_event);\n this.canvas_div = canvas_div\n this._canvas_extra_style(canvas_div)\n this.root.append(canvas_div);\n\n var canvas = $('');\n canvas.addClass('mpl-canvas');\n canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n\n this.canvas = canvas[0];\n this.context = canvas[0].getContext(\"2d\");\n\n var backingStore = this.context.backingStorePixelRatio ||\n\tthis.context.webkitBackingStorePixelRatio ||\n\tthis.context.mozBackingStorePixelRatio ||\n\tthis.context.msBackingStorePixelRatio ||\n\tthis.context.oBackingStorePixelRatio ||\n\tthis.context.backingStorePixelRatio || 1;\n\n mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n var rubberband = $('');\n rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n\n var pass_mouse_events = true;\n\n canvas_div.resizable({\n start: function(event, ui) {\n pass_mouse_events = false;\n },\n resize: function(event, ui) {\n fig.request_resize(ui.size.width, ui.size.height);\n },\n stop: function(event, ui) {\n pass_mouse_events = true;\n fig.request_resize(ui.size.width, ui.size.height);\n },\n });\n\n function mouse_event_fn(event) {\n if (pass_mouse_events)\n return fig.mouse_event(event, event['data']);\n }\n\n rubberband.mousedown('button_press', mouse_event_fn);\n rubberband.mouseup('button_release', mouse_event_fn);\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband.mousemove('motion_notify', mouse_event_fn);\n\n rubberband.mouseenter('figure_enter', mouse_event_fn);\n rubberband.mouseleave('figure_leave', mouse_event_fn);\n\n canvas_div.on(\"wheel\", function (event) {\n event = event.originalEvent;\n event['data'] = 'scroll'\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n mouse_event_fn(event);\n });\n\n canvas_div.append(canvas);\n canvas_div.append(rubberband);\n\n this.rubberband = rubberband;\n this.rubberband_canvas = rubberband[0];\n this.rubberband_context = rubberband[0].getContext(\"2d\");\n this.rubberband_context.strokeStyle = \"#000000\";\n\n this._resize_canvas = function(width, height) {\n // Keep the size of the canvas, canvas container, and rubber band\n // canvas in synch.\n canvas_div.css('width', width)\n canvas_div.css('height', height)\n\n canvas.attr('width', width * mpl.ratio);\n canvas.attr('height', height * mpl.ratio);\n canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n\n rubberband.attr('width', width);\n rubberband.attr('height', height);\n }\n\n // Set the figure to an initial 600x600px, this will subsequently be updated\n // upon first draw.\n this._resize_canvas(600, 600);\n\n // Disable right mouse context menu.\n $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n return false;\n });\n\n function set_focus () {\n canvas.focus();\n canvas_div.focus();\n }\n\n window.setTimeout(set_focus, 100);\n}\n\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
');\n nav_element.attr('style', 'width: 100%');\n this.root.append(nav_element);\n\n // Define a callback function for later on.\n function toolbar_event(event) {\n return fig.toolbar_button_onclick(event['data']);\n }\n function toolbar_mouse_event(event) {\n return fig.toolbar_button_onmouseover(event['data']);\n }\n\n for(var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n // put a spacer in here.\n continue;\n }\n var button = $('');\n", + " button.click(method_name, toolbar_event);\n", + " button.mouseover(tooltip, toolbar_mouse_event);\n", + " nav_element.append(button);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = $('');\n", + " nav_element.append(status_bar);\n", + " this.message = status_bar[0];\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = $('
');\n", + " var button = $('');\n", + " button.click(function (evt) { fig.handle_close(fig, {}); } );\n", + " button.mouseover('Stop Interaction', toolbar_mouse_event);\n", + " buttongrp.append(button);\n", + " var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", + " titlebar.prepend(buttongrp);\n", + "}\n", + "\n", + "mpl.figure.prototype._root_extra_style = function(el){\n", + " var fig = this\n", + " el.on(\"remove\", function(){\n", + "\tfig.close_ws(fig, {});\n", + " });\n", + "}\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function(el){\n", + " // this is important to make the div 'focusable\n", + " el.attr('tabindex', 0)\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " }\n", + " else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "\n", + "}\n", + "\n", + "mpl.figure.prototype._key_event_extra = function(event, name) {\n", + " var manager = IPython.notebook.keyboard_manager;\n", + " if (!manager)\n", + " manager = IPython.keyboard_manager;\n", + "\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which == 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "}\n", + "\n", + "mpl.figure.prototype.handle_save = function(fig, msg) {\n", + " fig.ondownload(fig, null);\n", + "}\n", + "\n", + "\n", + "mpl.find_output_cell = function(html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i=0; i= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] == html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "}\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel != null) {\n", + " IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", + "}\n" + ], "text/plain": [ "" ] @@ -343,9 +1110,9 @@ ], "source": [ "%matplotlib notebook\n", - "visu = MotorDashboard(state_plots=['i_sq', 'i_sd', 'u_sq', 'u_sd'], update_interval=10), # visualization\n", + "visu = MotorDashboard(state_plots=['i_sq', 'i_sd', 'u_sq', 'u_sd'], update_interval=10) # visualization\n", "env = gem.make('AbcCont-CC-PMSM-v0',\n", - " visualization=visu\n", + " visualization=visu,\n", " load=ConstantSpeedLoad(omega_fixed=1000 * np.pi / 30), # Fixed preset speed\n", " ode_solver='scipy.solve_ivp', # ODE Solver of the simulation\n", " reference_generator=reference_generator, # initialize the reference generators\n", @@ -407,7 +1174,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.9.5" } }, "nbformat": 4, diff --git a/examples/reinforcement_learning_controllers/stable_baselines3_dqn_disc_pmsm_example.ipynb b/examples/reinforcement_learning_controllers/stable_baselines3_dqn_disc_pmsm_example.ipynb index d37c9dfc..745cf3bf 100644 --- a/examples/reinforcement_learning_controllers/stable_baselines3_dqn_disc_pmsm_example.ipynb +++ b/examples/reinforcement_learning_controllers/stable_baselines3_dqn_disc_pmsm_example.ipynb @@ -99,16 +99,7 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\arnet\\anaconda3\\envs\\gem_dev\\lib\\site-packages\\gym\\logger.py:34: UserWarning: \u001b[33mWARN: Box bound precision lowered by casting to float32\u001b[0m\n", - " warnings.warn(colorize(\"%s: %s\" % (\"WARN\", msg % args), \"yellow\"))\n" - ] - } - ], + "outputs": [], "source": [ "%matplotlib notebook\n", "# Use %matplotlib widget in Visual Studio Code\n", @@ -127,7 +118,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 2, "metadata": { "scrolled": false }, @@ -215,9 +206,18 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\arnet\\anaconda3\\envs\\gem_dev\\lib\\site-packages\\gym\\logger.py:34: UserWarning: \u001b[33mWARN: Box bound precision lowered by casting to float32\u001b[0m\n", + " warnings.warn(colorize(\"%s: %s\" % (\"WARN\", msg % args), \"yellow\"))\n" + ] + } + ], "source": [ "# define motor arguments\n", "motor_parameter = dict(\n", @@ -337,7 +337,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -363,7 +363,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -389,7 +389,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -414,16 +414,7 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "motor_dashboard.initialize()" - ] - }, - { - "cell_type": "code", - "execution_count": 46, + "execution_count": 7, "metadata": { "scrolled": true }, @@ -437,231 +428,242 @@ "Wrapping the env in a DummyVecEnv.\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 365 |\n", - "| ep_rew_mean | -3.26e+03 |\n", - "| exploration_rate | 0.972 |\n", + "| ep_len_mean | 239 |\n", + "| ep_rew_mean | -3.12e+03 |\n", + "| exploration_rate | 0.982 |\n", "| time/ | |\n", "| episodes | 4 |\n", - "| fps | 2402 |\n", + "| fps | 2328 |\n", "| time_elapsed | 0 |\n", - "| total_timesteps | 1461 |\n", + "| total_timesteps | 957 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 485 |\n", - "| ep_rew_mean | -3.96e+03 |\n", - "| exploration_rate | 0.926 |\n", + "| ep_len_mean | 458 |\n", + "| ep_rew_mean | -4.27e+03 |\n", + "| exploration_rate | 0.93 |\n", "| time/ | |\n", "| episodes | 8 |\n", - "| fps | 2398 |\n", + "| fps | 2424 |\n", "| time_elapsed | 1 |\n", - "| total_timesteps | 3881 |\n", + "| total_timesteps | 3668 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 449 |\n", - "| ep_rew_mean | -3.73e+03 |\n", - "| exploration_rate | 0.898 |\n", + "| ep_len_mean | 445 |\n", + "| ep_rew_mean | -4.07e+03 |\n", + "| exploration_rate | 0.899 |\n", "| time/ | |\n", "| episodes | 12 |\n", - "| fps | 2398 |\n", + "| fps | 2505 |\n", "| time_elapsed | 2 |\n", - "| total_timesteps | 5387 |\n", + "| total_timesteps | 5341 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 727 |\n", - "| ep_rew_mean | -5.37e+03 |\n", - "| exploration_rate | 0.779 |\n", + "| ep_len_mean | 548 |\n", + "| ep_rew_mean | -4.28e+03 |\n", + "| exploration_rate | 0.833 |\n", "| time/ | |\n", "| episodes | 16 |\n", - "| fps | 1473 |\n", - "| time_elapsed | 7 |\n", - "| total_timesteps | 11631 |\n", - "| train/ | |\n", - "| learning_rate | 0.0001 |\n", - "| loss | 0.98 |\n", - "| n_updates | 1630 |\n", + "| fps | 2564 |\n", + "| time_elapsed | 3 |\n", + "| total_timesteps | 8771 |\n", "-----------------------------------\n", "----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 640 |\n", - "| ep_rew_mean | -4.9e+03 |\n", - "| exploration_rate | 0.757 |\n", + "| ep_len_mean | 526 |\n", + "| ep_rew_mean | -4.2e+03 |\n", + "| exploration_rate | 0.8 |\n", "| time/ | |\n", "| episodes | 20 |\n", - "| fps | 1207 |\n", - "| time_elapsed | 10 |\n", - "| total_timesteps | 12803 |\n", + "| fps | 2124 |\n", + "| time_elapsed | 4 |\n", + "| total_timesteps | 10528 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 0.462 |\n", - "| n_updates | 2802 |\n", - "----------------------------------\n", - "----------------------------------\n", - "| rollout/ | |\n", - "| ep_len_mean | 591 |\n", - "| ep_rew_mean | -4.6e+03 |\n", - "| exploration_rate | 0.731 |\n", - "| time/ | |\n", - "| episodes | 24 |\n", - "| fps | 1007 |\n", - "| time_elapsed | 14 |\n", - "| total_timesteps | 14178 |\n", - "| train/ | |\n", - "| learning_rate | 0.0001 |\n", - "| loss | 0.708 |\n", - "| n_updates | 4177 |\n", + "| loss | 1.54 |\n", + "| n_updates | 527 |\n", "----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 552 |\n", - "| ep_rew_mean | -4.37e+03 |\n", - "| exploration_rate | 0.706 |\n", + "| ep_len_mean | 488 |\n", + "| ep_rew_mean | -4.06e+03 |\n", + "| exploration_rate | 0.777 |\n", + "| time/ | |\n", + "| episodes | 24 |\n", + "| fps | 1530 |\n", + "| time_elapsed | 7 |\n", + "| total_timesteps | 11717 |\n", + "| train/ | |\n", + "| learning_rate | 0.0001 |\n", + "| loss | 0.793 |\n", + "| n_updates | 1716 |\n", + "-----------------------------------\n", + "-----------------------------------\n", + "| rollout/ | |\n", + "| ep_len_mean | 446 |\n", + "| ep_rew_mean | -3.99e+03 |\n", + "| exploration_rate | 0.763 |\n", "| time/ | |\n", "| episodes | 28 |\n", - "| fps | 897 |\n", - "| time_elapsed | 17 |\n", - "| total_timesteps | 15463 |\n", + "| fps | 1324 |\n", + "| time_elapsed | 9 |\n", + "| total_timesteps | 12485 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 0.566 |\n", - "| n_updates | 5462 |\n", + "| loss | 0.745 |\n", + "| n_updates | 2484 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 535 |\n", - "| ep_rew_mean | -4.31e+03 |\n", - "| exploration_rate | 0.675 |\n", + "| ep_len_mean | 448 |\n", + "| ep_rew_mean | -4.05e+03 |\n", + "| exploration_rate | 0.728 |\n", "| time/ | |\n", "| episodes | 32 |\n", - "| fps | 799 |\n", - "| time_elapsed | 21 |\n", - "| total_timesteps | 17114 |\n", + "| fps | 1069 |\n", + "| time_elapsed | 13 |\n", + "| total_timesteps | 14332 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 0.602 |\n", - "| n_updates | 7113 |\n", + "| loss | 0.906 |\n", + "| n_updates | 4331 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 798 |\n", - "| ep_rew_mean | -5.27e+03 |\n", - "| exploration_rate | 0.454 |\n", + "| ep_len_mean | 455 |\n", + "| ep_rew_mean | -4.01e+03 |\n", + "| exploration_rate | 0.688 |\n", "| time/ | |\n", "| episodes | 36 |\n", - "| fps | 581 |\n", - "| time_elapsed | 49 |\n", - "| total_timesteps | 28745 |\n", + "| fps | 913 |\n", + "| time_elapsed | 17 |\n", + "| total_timesteps | 16395 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 0.86 |\n", - "| n_updates | 18744 |\n", + "| loss | 1.36 |\n", + "| n_updates | 6394 |\n", "-----------------------------------\n", - "----------------------------------\n", - "| rollout/ | |\n", - "| ep_len_mean | 1.48e+03 |\n", - "| ep_rew_mean | -7.3e+03 |\n", - "| exploration_rate | 0.05 |\n", - "| time/ | |\n", - "| episodes | 40 |\n", - "| fps | 472 |\n", - "| time_elapsed | 125 |\n", - "| total_timesteps | 59259 |\n", - "| train/ | |\n", - "| learning_rate | 0.0001 |\n", - "| loss | 75.9 |\n", - "| n_updates | 49258 |\n", - "----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 2.06e+03 |\n", - "| ep_rew_mean | -8.66e+03 |\n", - "| exploration_rate | 0.05 |\n", + "| ep_len_mean | 435 |\n", + "| ep_rew_mean | -3.94e+03 |\n", + "| exploration_rate | 0.669 |\n", "| time/ | |\n", - "| episodes | 44 |\n", - "| fps | 435 |\n", - "| time_elapsed | 207 |\n", - "| total_timesteps | 90574 |\n", + "| episodes | 40 |\n", + "| fps | 862 |\n", + "| time_elapsed | 20 |\n", + "| total_timesteps | 17407 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 4 |\n", - "| n_updates | 80573 |\n", + "| loss | 0.749 |\n", + "| n_updates | 7406 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 2.53e+03 |\n", - "| ep_rew_mean | -9.94e+03 |\n", - "| exploration_rate | 0.05 |\n", + "| ep_len_mean | 465 |\n", + "| ep_rew_mean | -4.21e+03 |\n", + "| exploration_rate | 0.611 |\n", "| time/ | |\n", - "| episodes | 48 |\n", - "| fps | 423 |\n", - "| time_elapsed | 286 |\n", - "| total_timesteps | 121337 |\n", + "| episodes | 44 |\n", + "| fps | 762 |\n", + "| time_elapsed | 26 |\n", + "| total_timesteps | 20472 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 4.46 |\n", - "| n_updates | 111336 |\n", + "| loss | 0.531 |\n", + "| n_updates | 10471 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 3.09e+03 |\n", - "| ep_rew_mean | -1.16e+04 |\n", - "| exploration_rate | 0.05 |\n", + "| ep_len_mean | 717 |\n", + "| ep_rew_mean | -4.99e+03 |\n", + "| exploration_rate | 0.346 |\n", "| time/ | |\n", - "| episodes | 52 |\n", - "| fps | 417 |\n", - "| time_elapsed | 385 |\n", - "| total_timesteps | 160849 |\n", + "| episodes | 48 |\n", + "| fps | 586 |\n", + "| time_elapsed | 58 |\n", + "| total_timesteps | 34411 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 3.67 |\n", - "| n_updates | 150848 |\n", + "| loss | 0.673 |\n", + "| n_updates | 24410 |\n", "-----------------------------------\n", + "----------------------------------\n", + "| rollout/ | |\n", + "| ep_len_mean | 717 |\n", + "| ep_rew_mean | -5e+03 |\n", + "| exploration_rate | 0.292 |\n", + "| time/ | |\n", + "| episodes | 52 |\n", + "| fps | 570 |\n", + "| time_elapsed | 65 |\n", + "| total_timesteps | 37269 |\n", + "| train/ | |\n", + "| learning_rate | 0.0001 |\n", + "| loss | 2 |\n", + "| n_updates | 27268 |\n", + "----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 3.43e+03 |\n", - "| ep_rew_mean | -1.19e+04 |\n", + "| ep_len_mean | 1.21e+03 |\n", + "| ep_rew_mean | -6.35e+03 |\n", "| exploration_rate | 0.05 |\n", "| time/ | |\n", "| episodes | 56 |\n", - "| fps | 413 |\n", - "| time_elapsed | 464 |\n", - "| total_timesteps | 192356 |\n", + "| fps | 471 |\n", + "| time_elapsed | 143 |\n", + "| total_timesteps | 67607 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 3.23 |\n", - "| n_updates | 182355 |\n", + "| loss | 5.86 |\n", + "| n_updates | 57606 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 3.87e+03 |\n", - "| ep_rew_mean | -1.24e+04 |\n", + "| ep_len_mean | 1.63e+03 |\n", + "| ep_rew_mean | -6.93e+03 |\n", "| exploration_rate | 0.05 |\n", "| time/ | |\n", "| episodes | 60 |\n", - "| fps | 410 |\n", - "| time_elapsed | 565 |\n", - "| total_timesteps | 232356 |\n", + "| fps | 454 |\n", + "| time_elapsed | 214 |\n", + "| total_timesteps | 97589 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 5.54 |\n", - "| n_updates | 222355 |\n", + "| loss | 2.25 |\n", + "| n_updates | 87588 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 4.01e+03 |\n", - "| ep_rew_mean | -1.23e+04 |\n", + "| ep_len_mean | 2.12e+03 |\n", + "| ep_rew_mean | -7.64e+03 |\n", "| exploration_rate | 0.05 |\n", "| time/ | |\n", "| episodes | 64 |\n", - "| fps | 409 |\n", - "| time_elapsed | 627 |\n", - "| total_timesteps | 256918 |\n", + "| fps | 444 |\n", + "| time_elapsed | 305 |\n", + "| total_timesteps | 135782 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 2.98 |\n", - "| n_updates | 246917 |\n", + "| loss | 5.12 |\n", + "| n_updates | 125781 |\n", + "-----------------------------------\n", + "-----------------------------------\n", + "| rollout/ | |\n", + "| ep_len_mean | 2.59e+03 |\n", + "| ep_rew_mean | -8.27e+03 |\n", + "| exploration_rate | 0.05 |\n", + "| time/ | |\n", + "| episodes | 68 |\n", + "| fps | 439 |\n", + "| time_elapsed | 400 |\n", + "| total_timesteps | 175782 |\n", + "| train/ | |\n", + "| learning_rate | 0.0001 |\n", + "| loss | 4.96 |\n", + "| n_updates | 165781 |\n", "-----------------------------------\n" ] }, @@ -671,155 +673,195 @@ "text": [ "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 3.96e+03 |\n", - "| ep_rew_mean | -1.19e+04 |\n", + "| ep_len_mean | 2.68e+03 |\n", + "| ep_rew_mean | -8.25e+03 |\n", "| exploration_rate | 0.05 |\n", "| time/ | |\n", - "| episodes | 68 |\n", - "| fps | 409 |\n", - "| time_elapsed | 658 |\n", - "| total_timesteps | 269515 |\n", + "| episodes | 72 |\n", + "| fps | 438 |\n", + "| time_elapsed | 441 |\n", + "| total_timesteps | 193227 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 5.49 |\n", - "| n_updates | 259514 |\n", + "| loss | 5.64 |\n", + "| n_updates | 183226 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 3.92e+03 |\n", - "| ep_rew_mean | -1.16e+04 |\n", + "| ep_len_mean | 3.01e+03 |\n", + "| ep_rew_mean | -8.31e+03 |\n", "| exploration_rate | 0.05 |\n", "| time/ | |\n", - "| episodes | 72 |\n", - "| fps | 408 |\n", - "| time_elapsed | 691 |\n", - "| total_timesteps | 282491 |\n", + "| episodes | 76 |\n", + "| fps | 434 |\n", + "| time_elapsed | 525 |\n", + "| total_timesteps | 228680 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 8.18 |\n", - "| n_updates | 272490 |\n", + "| loss | 2.42 |\n", + "| n_updates | 218679 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 3.9e+03 |\n", - "| ep_rew_mean | -1.14e+04 |\n", + "| ep_len_mean | 3.24e+03 |\n", + "| ep_rew_mean | -8.35e+03 |\n", "| exploration_rate | 0.05 |\n", "| time/ | |\n", - "| episodes | 76 |\n", - "| fps | 407 |\n", - "| time_elapsed | 728 |\n", - "| total_timesteps | 296775 |\n", + "| episodes | 80 |\n", + "| fps | 432 |\n", + "| time_elapsed | 598 |\n", + "| total_timesteps | 258806 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 3.79 |\n", - "| n_updates | 286774 |\n", + "| loss | 2.84 |\n", + "| n_updates | 248805 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 4.01e+03 |\n", - "| ep_rew_mean | -1.12e+04 |\n", + "| ep_len_mean | 3.33e+03 |\n", + "| ep_rew_mean | -8.24e+03 |\n", "| exploration_rate | 0.05 |\n", "| time/ | |\n", - "| episodes | 80 |\n", - "| fps | 405 |\n", - "| time_elapsed | 791 |\n", - "| total_timesteps | 321023 |\n", + "| episodes | 84 |\n", + "| fps | 432 |\n", + "| time_elapsed | 647 |\n", + "| total_timesteps | 280019 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 3.89 |\n", - "| n_updates | 311022 |\n", + "| loss | 3.06 |\n", + "| n_updates | 270018 |\n", "-----------------------------------\n", - "----------------------------------\n", - "| rollout/ | |\n", - "| ep_len_mean | 4.06e+03 |\n", - "| ep_rew_mean | -1.1e+04 |\n", - "| exploration_rate | 0.05 |\n", - "| time/ | |\n", - "| episodes | 84 |\n", - "| fps | 405 |\n", - "| time_elapsed | 841 |\n", - "| total_timesteps | 340990 |\n", - "| train/ | |\n", - "| learning_rate | 0.0001 |\n", - "| loss | 3.9 |\n", - "| n_updates | 330989 |\n", - "----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 4.02e+03 |\n", - "| ep_rew_mean | -1.07e+04 |\n", + "| ep_len_mean | 3.39e+03 |\n", + "| ep_rew_mean | -8.28e+03 |\n", "| exploration_rate | 0.05 |\n", "| time/ | |\n", "| episodes | 88 |\n", - "| fps | 405 |\n", - "| time_elapsed | 872 |\n", - "| total_timesteps | 353446 |\n", + "| fps | 432 |\n", + "| time_elapsed | 690 |\n", + "| total_timesteps | 298621 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 2.26 |\n", - "| n_updates | 343445 |\n", + "| loss | 3.01 |\n", + "| n_updates | 288620 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 4.09e+03 |\n", - "| ep_rew_mean | -1.06e+04 |\n", + "| ep_len_mean | 3.59e+03 |\n", + "| ep_rew_mean | -8.48e+03 |\n", "| exploration_rate | 0.05 |\n", "| time/ | |\n", "| episodes | 92 |\n", - "| fps | 404 |\n", - "| time_elapsed | 929 |\n", - "| total_timesteps | 376420 |\n", + "| fps | 431 |\n", + "| time_elapsed | 765 |\n", + "| total_timesteps | 330553 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 4.61 |\n", - "| n_updates | 366419 |\n", + "| loss | 3.68 |\n", + "| n_updates | 320552 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 4.19e+03 |\n", - "| ep_rew_mean | -1.04e+04 |\n", + "| ep_len_mean | 3.47e+03 |\n", + "| ep_rew_mean | -8.26e+03 |\n", "| exploration_rate | 0.05 |\n", "| time/ | |\n", "| episodes | 96 |\n", - "| fps | 404 |\n", - "| time_elapsed | 993 |\n", - "| total_timesteps | 401970 |\n", + "| fps | 431 |\n", + "| time_elapsed | 770 |\n", + "| total_timesteps | 332774 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 2.41 |\n", - "| n_updates | 391969 |\n", + "| loss | 2.38 |\n", + "| n_updates | 322773 |\n", "-----------------------------------\n", "-----------------------------------\n", "| rollout/ | |\n", - "| ep_len_mean | 4.18e+03 |\n", - "| ep_rew_mean | -1.03e+04 |\n", + "| ep_len_mean | 3.59e+03 |\n", + "| ep_rew_mean | -8.26e+03 |\n", "| exploration_rate | 0.05 |\n", "| time/ | |\n", "| episodes | 100 |\n", - "| fps | 403 |\n", - "| time_elapsed | 1035 |\n", - "| total_timesteps | 418177 |\n", + "| fps | 430 |\n", + "| time_elapsed | 832 |\n", + "| total_timesteps | 358736 |\n", + "| train/ | |\n", + "| learning_rate | 0.0001 |\n", + "| loss | 4.2 |\n", + "| n_updates | 348735 |\n", + "-----------------------------------\n", + "----------------------------------\n", + "| rollout/ | |\n", + "| ep_len_mean | 3.88e+03 |\n", + "| ep_rew_mean | -8.5e+03 |\n", + "| exploration_rate | 0.05 |\n", + "| time/ | |\n", + "| episodes | 104 |\n", + "| fps | 430 |\n", + "| time_elapsed | 903 |\n", + "| total_timesteps | 389397 |\n", + "| train/ | |\n", + "| learning_rate | 0.0001 |\n", + "| loss | 4.78 |\n", + "| n_updates | 379396 |\n", + "----------------------------------\n", + "-----------------------------------\n", + "| rollout/ | |\n", + "| ep_len_mean | 4.16e+03 |\n", + "| ep_rew_mean | -8.63e+03 |\n", + "| exploration_rate | 0.05 |\n", + "| time/ | |\n", + "| episodes | 108 |\n", + "| fps | 428 |\n", + "| time_elapsed | 977 |\n", + "| total_timesteps | 419202 |\n", + "| train/ | |\n", + "| learning_rate | 0.0001 |\n", + "| loss | 2.16 |\n", + "| n_updates | 409201 |\n", + "-----------------------------------\n", + "-----------------------------------\n", + "| rollout/ | |\n", + "| ep_len_mean | 4.49e+03 |\n", + "| ep_rew_mean | -8.81e+03 |\n", + "| exploration_rate | 0.05 |\n", + "| time/ | |\n", + "| episodes | 112 |\n", + "| fps | 427 |\n", + "| time_elapsed | 1062 |\n", + "| total_timesteps | 454123 |\n", + "| train/ | |\n", + "| learning_rate | 0.0001 |\n", + "| loss | 1.98 |\n", + "| n_updates | 444122 |\n", + "-----------------------------------\n", + "-----------------------------------\n", + "| rollout/ | |\n", + "| ep_len_mean | 4.63e+03 |\n", + "| ep_rew_mean | -8.86e+03 |\n", + "| exploration_rate | 0.05 |\n", + "| time/ | |\n", + "| episodes | 116 |\n", + "| fps | 426 |\n", + "| time_elapsed | 1106 |\n", + "| total_timesteps | 471392 |\n", "| train/ | |\n", "| learning_rate | 0.0001 |\n", - "| loss | 2.14 |\n", - "| n_updates | 408176 |\n", + "| loss | 4.11 |\n", + "| n_updates | 461391 |\n", "-----------------------------------\n" ] }, { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[0mexploration_fraction\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mexploration_fraction\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtarget_update_interval\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mtarget_update_interval\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m verbose=verbose)\n\u001b[1;32m----> 5\u001b[1;33m \u001b[0mmodel\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mlearn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mtotal_timesteps\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mnb_steps\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[1;32m~\\anaconda3\\envs\\gem_dev\\lib\\site-packages\\stable_baselines3\\dqn\\dqn.py\u001b[0m in \u001b[0;36mlearn\u001b[1;34m(self, total_timesteps, callback, log_interval, eval_env, eval_freq, n_eval_episodes, tb_log_name, eval_log_path, reset_num_timesteps)\u001b[0m\n\u001b[0;32m 239\u001b[0m ) -> OffPolicyAlgorithm:\n\u001b[0;32m 240\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 241\u001b[1;33m return super(DQN, self).learn(\n\u001b[0m\u001b[0;32m 242\u001b[0m \u001b[0mtotal_timesteps\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mtotal_timesteps\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 243\u001b[0m \u001b[0mcallback\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mcallback\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32m~\\anaconda3\\envs\\gem_dev\\lib\\site-packages\\stable_baselines3\\common\\off_policy_algorithm.py\u001b[0m in \u001b[0;36mlearn\u001b[1;34m(self, total_timesteps, callback, log_interval, eval_env, eval_freq, n_eval_episodes, tb_log_name, eval_log_path, reset_num_timesteps)\u001b[0m\n\u001b[0;32m 369\u001b[0m \u001b[1;31m# Special case when the user passes `gradient_steps=0`\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 370\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mgradient_steps\u001b[0m \u001b[1;33m>\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 371\u001b[1;33m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mtrain\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mbatch_size\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mbatch_size\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mgradient_steps\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mgradient_steps\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 372\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 373\u001b[0m \u001b[0mcallback\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mon_training_end\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32m~\\anaconda3\\envs\\gem_dev\\lib\\site-packages\\stable_baselines3\\dqn\\dqn.py\u001b[0m in \u001b[0;36mtrain\u001b[1;34m(self, gradient_steps, batch_size)\u001b[0m\n\u001b[0;32m 187\u001b[0m \u001b[0mloss\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 188\u001b[0m \u001b[1;31m# Clip gradient norm\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 189\u001b[1;33m \u001b[0mth\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mnn\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mutils\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mclip_grad_norm_\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpolicy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mparameters\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmax_grad_norm\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 190\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpolicy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0moptimizer\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 191\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;32m~\\anaconda3\\envs\\gem_dev\\lib\\site-packages\\torch\\nn\\utils\\clip_grad.py\u001b[0m in \u001b[0;36mclip_grad_norm_\u001b[1;34m(parameters, max_norm, norm_type, error_if_nonfinite)\u001b[0m\n\u001b[0;32m 55\u001b[0m FutureWarning, stacklevel=2)\n\u001b[0;32m 56\u001b[0m \u001b[0mclip_coef\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mmax_norm\u001b[0m \u001b[1;33m/\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mtotal_norm\u001b[0m \u001b[1;33m+\u001b[0m \u001b[1;36m1e-6\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 57\u001b[1;33m \u001b[1;32mif\u001b[0m \u001b[0mclip_coef\u001b[0m \u001b[1;33m<\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 58\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mp\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mparameters\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 59\u001b[0m \u001b[0mp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgrad\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdetach\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmul_\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mclip_coef\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mto\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mp\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mgrad\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdevice\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;31mKeyboardInterrupt\u001b[0m: " - ] + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -846,7 +888,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -885,7 +927,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -908,12 +950,967 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 14, "metadata": {}, "outputs": [ { "data": { - "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.get_websocket_type = function () {\n if (typeof WebSocket !== 'undefined') {\n return WebSocket;\n } else if (typeof MozWebSocket !== 'undefined') {\n return MozWebSocket;\n } else {\n alert(\n 'Your browser does not have WebSocket support. ' +\n 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n 'Firefox 4 and 5 are also supported but you ' +\n 'have to enable WebSockets in about:config.'\n );\n }\n};\n\nmpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n this.id = figure_id;\n\n this.ws = websocket;\n\n this.supports_binary = this.ws.binaryType !== undefined;\n\n if (!this.supports_binary) {\n var warnings = document.getElementById('mpl-warnings');\n if (warnings) {\n warnings.style.display = 'block';\n warnings.textContent =\n 'This browser does not support binary websocket messages. ' +\n 'Performance may be slow.';\n }\n }\n\n this.imageObj = new Image();\n\n this.context = undefined;\n this.message = undefined;\n this.canvas = undefined;\n this.rubberband_canvas = undefined;\n this.rubberband_context = undefined;\n this.format_dropdown = undefined;\n\n this.image_mode = 'full';\n\n this.root = document.createElement('div');\n this.root.setAttribute('style', 'display: inline-block');\n this._root_extra_style(this.root);\n\n parent_element.appendChild(this.root);\n\n this._init_header(this);\n this._init_canvas(this);\n this._init_toolbar(this);\n\n var fig = this;\n\n this.waiting = false;\n\n this.ws.onopen = function () {\n fig.send_message('supports_binary', { value: fig.supports_binary });\n fig.send_message('send_image_mode', {});\n if (fig.ratio !== 1) {\n fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n }\n fig.send_message('refresh', {});\n };\n\n this.imageObj.onload = function () {\n if (fig.image_mode === 'full') {\n // Full images could contain transparency (where diff images\n // almost always do), so we need to clear the canvas so that\n // there is no ghosting.\n fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n }\n fig.context.drawImage(fig.imageObj, 0, 0);\n };\n\n this.imageObj.onunload = function () {\n fig.ws.close();\n };\n\n this.ws.onmessage = this._make_on_message_function(this);\n\n this.ondownload = ondownload;\n};\n\nmpl.figure.prototype._init_header = function () {\n var titlebar = document.createElement('div');\n titlebar.classList =\n 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n var titletext = document.createElement('div');\n titletext.classList = 'ui-dialog-title';\n titletext.setAttribute(\n 'style',\n 'width: 100%; text-align: center; padding: 3px;'\n );\n titlebar.appendChild(titletext);\n this.root.appendChild(titlebar);\n this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n var fig = this;\n\n var canvas_div = (this.canvas_div = document.createElement('div'));\n canvas_div.setAttribute(\n 'style',\n 'border: 1px solid #ddd;' +\n 'box-sizing: content-box;' +\n 'clear: both;' +\n 'min-height: 1px;' +\n 'min-width: 1px;' +\n 'outline: 0;' +\n 'overflow: hidden;' +\n 'position: relative;' +\n 'resize: both;'\n );\n\n function on_keyboard_event_closure(name) {\n return function (event) {\n return fig.key_event(event, name);\n };\n }\n\n canvas_div.addEventListener(\n 'keydown',\n on_keyboard_event_closure('key_press')\n );\n canvas_div.addEventListener(\n 'keyup',\n on_keyboard_event_closure('key_release')\n );\n\n this._canvas_extra_style(canvas_div);\n this.root.appendChild(canvas_div);\n\n var canvas = (this.canvas = document.createElement('canvas'));\n canvas.classList.add('mpl-canvas');\n canvas.setAttribute('style', 'box-sizing: content-box;');\n\n this.context = canvas.getContext('2d');\n\n var backingStore =\n this.context.backingStorePixelRatio ||\n this.context.webkitBackingStorePixelRatio ||\n this.context.mozBackingStorePixelRatio ||\n this.context.msBackingStorePixelRatio ||\n this.context.oBackingStorePixelRatio ||\n this.context.backingStorePixelRatio ||\n 1;\n\n this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n 'canvas'\n ));\n rubberband_canvas.setAttribute(\n 'style',\n 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n );\n\n // Apply a ponyfill if ResizeObserver is not implemented by browser.\n if (this.ResizeObserver === undefined) {\n if (window.ResizeObserver !== undefined) {\n this.ResizeObserver = window.ResizeObserver;\n } else {\n var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n this.ResizeObserver = obs.ResizeObserver;\n }\n }\n\n this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n var nentries = entries.length;\n for (var i = 0; i < nentries; i++) {\n var entry = entries[i];\n var width, height;\n if (entry.contentBoxSize) {\n if (entry.contentBoxSize instanceof Array) {\n // Chrome 84 implements new version of spec.\n width = entry.contentBoxSize[0].inlineSize;\n height = entry.contentBoxSize[0].blockSize;\n } else {\n // Firefox implements old version of spec.\n width = entry.contentBoxSize.inlineSize;\n height = entry.contentBoxSize.blockSize;\n }\n } else {\n // Chrome <84 implements even older version of spec.\n width = entry.contentRect.width;\n height = entry.contentRect.height;\n }\n\n // Keep the size of the canvas and rubber band canvas in sync with\n // the canvas container.\n if (entry.devicePixelContentBoxSize) {\n // Chrome 84 implements new version of spec.\n canvas.setAttribute(\n 'width',\n entry.devicePixelContentBoxSize[0].inlineSize\n );\n canvas.setAttribute(\n 'height',\n entry.devicePixelContentBoxSize[0].blockSize\n );\n } else {\n canvas.setAttribute('width', width * fig.ratio);\n canvas.setAttribute('height', height * fig.ratio);\n }\n canvas.setAttribute(\n 'style',\n 'width: ' + width + 'px; height: ' + height + 'px;'\n );\n\n rubberband_canvas.setAttribute('width', width);\n rubberband_canvas.setAttribute('height', height);\n\n // And update the size in Python. We ignore the initial 0/0 size\n // that occurs as the element is placed into the DOM, which should\n // otherwise not happen due to the minimum size styling.\n if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n fig.request_resize(width, height);\n }\n }\n });\n this.resizeObserverInstance.observe(canvas_div);\n\n function on_mouse_event_closure(name) {\n return function (event) {\n return fig.mouse_event(event, name);\n };\n }\n\n rubberband_canvas.addEventListener(\n 'mousedown',\n on_mouse_event_closure('button_press')\n );\n rubberband_canvas.addEventListener(\n 'mouseup',\n on_mouse_event_closure('button_release')\n );\n rubberband_canvas.addEventListener(\n 'dblclick',\n on_mouse_event_closure('dblclick')\n );\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband_canvas.addEventListener(\n 'mousemove',\n on_mouse_event_closure('motion_notify')\n );\n\n rubberband_canvas.addEventListener(\n 'mouseenter',\n on_mouse_event_closure('figure_enter')\n );\n rubberband_canvas.addEventListener(\n 'mouseleave',\n on_mouse_event_closure('figure_leave')\n );\n\n canvas_div.addEventListener('wheel', function (event) {\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n on_mouse_event_closure('scroll')(event);\n });\n\n canvas_div.appendChild(canvas);\n canvas_div.appendChild(rubberband_canvas);\n\n this.rubberband_context = rubberband_canvas.getContext('2d');\n this.rubberband_context.strokeStyle = '#000000';\n\n this._resize_canvas = function (width, height, forward) {\n if (forward) {\n canvas_div.style.width = width + 'px';\n canvas_div.style.height = height + 'px';\n }\n };\n\n // Disable right mouse context menu.\n this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n event.preventDefault();\n return false;\n });\n\n function set_focus() {\n canvas.focus();\n canvas_div.focus();\n }\n\n window.setTimeout(set_focus, 100);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'mpl-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n for (var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n continue;\n }\n\n var button = (fig.buttons[name] = document.createElement('button'));\n button.classList = 'mpl-widget';\n button.setAttribute('role', 'button');\n button.setAttribute('aria-disabled', 'false');\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n var icon_img = document.createElement('img');\n icon_img.src = '_images/' + image + '.png';\n icon_img.srcset = '_images/' + image + '_large.png 2x';\n icon_img.alt = tooltip;\n button.appendChild(icon_img);\n\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n var fmt_picker = document.createElement('select');\n fmt_picker.classList = 'mpl-widget';\n toolbar.appendChild(fmt_picker);\n this.format_dropdown = fmt_picker;\n\n for (var ind in mpl.extensions) {\n var fmt = mpl.extensions[ind];\n var option = document.createElement('option');\n option.selected = fmt === mpl.default_extension;\n option.innerHTML = fmt;\n fmt_picker.appendChild(option);\n }\n\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n};\n\nmpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n // which will in turn request a refresh of the image.\n this.send_message('resize', { width: x_pixels, height: y_pixels });\n};\n\nmpl.figure.prototype.send_message = function (type, properties) {\n properties['type'] = type;\n properties['figure_id'] = this.id;\n this.ws.send(JSON.stringify(properties));\n};\n\nmpl.figure.prototype.send_draw_message = function () {\n if (!this.waiting) {\n this.waiting = true;\n this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n var format_dropdown = fig.format_dropdown;\n var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n fig.ondownload(fig, format);\n};\n\nmpl.figure.prototype.handle_resize = function (fig, msg) {\n var size = msg['size'];\n if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n fig._resize_canvas(size[0], size[1], msg['forward']);\n fig.send_message('refresh', {});\n }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n var x0 = msg['x0'] / fig.ratio;\n var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n var x1 = msg['x1'] / fig.ratio;\n var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n x0 = Math.floor(x0) + 0.5;\n y0 = Math.floor(y0) + 0.5;\n x1 = Math.floor(x1) + 0.5;\n y1 = Math.floor(y1) + 0.5;\n var min_x = Math.min(x0, x1);\n var min_y = Math.min(y0, y1);\n var width = Math.abs(x1 - x0);\n var height = Math.abs(y1 - y0);\n\n fig.rubberband_context.clearRect(\n 0,\n 0,\n fig.canvas.width / fig.ratio,\n fig.canvas.height / fig.ratio\n );\n\n fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n var cursor = msg['cursor'];\n switch (cursor) {\n case 0:\n cursor = 'pointer';\n break;\n case 1:\n cursor = 'default';\n break;\n case 2:\n cursor = 'crosshair';\n break;\n case 3:\n cursor = 'move';\n break;\n }\n fig.rubberband_canvas.style.cursor = cursor;\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n for (var key in msg) {\n if (!(key in fig.buttons)) {\n continue;\n }\n fig.buttons[key].disabled = !msg[key];\n fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n if (msg['mode'] === 'PAN') {\n fig.buttons['Pan'].classList.add('active');\n fig.buttons['Zoom'].classList.remove('active');\n } else if (msg['mode'] === 'ZOOM') {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.add('active');\n } else {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.remove('active');\n }\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n // Called whenever the canvas gets updated.\n this.send_message('ack', {});\n};\n\n// A function to construct a web socket function for onmessage handling.\n// Called in the figure constructor.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n return function socket_on_message(evt) {\n if (evt.data instanceof Blob) {\n var img = evt.data;\n if (img.type !== 'image/png') {\n /* FIXME: We get \"Resource interpreted as Image but\n * transferred with MIME type text/plain:\" errors on\n * Chrome. But how to set the MIME type? It doesn't seem\n * to be part of the websocket stream */\n img.type = 'image/png';\n }\n\n /* Free the memory for the previous frames */\n if (fig.imageObj.src) {\n (window.URL || window.webkitURL).revokeObjectURL(\n fig.imageObj.src\n );\n }\n\n fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n img\n );\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n } else if (\n typeof evt.data === 'string' &&\n evt.data.slice(0, 21) === 'data:image/png;base64'\n ) {\n fig.imageObj.src = evt.data;\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n }\n\n var msg = JSON.parse(evt.data);\n var msg_type = msg['type'];\n\n // Call the \"handle_{type}\" callback, which takes\n // the figure and JSON message as its only arguments.\n try {\n var callback = fig['handle_' + msg_type];\n } catch (e) {\n console.log(\n \"No handler for the '\" + msg_type + \"' message type: \",\n msg\n );\n return;\n }\n\n if (callback) {\n try {\n // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n callback(fig, msg);\n } catch (e) {\n console.log(\n \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n e,\n e.stack,\n msg\n );\n }\n }\n };\n};\n\n// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.findpos = function (e) {\n //this section is from http://www.quirksmode.org/js/events_properties.html\n var targ;\n if (!e) {\n e = window.event;\n }\n if (e.target) {\n targ = e.target;\n } else if (e.srcElement) {\n targ = e.srcElement;\n }\n if (targ.nodeType === 3) {\n // defeat Safari bug\n targ = targ.parentNode;\n }\n\n // pageX,Y are the mouse positions relative to the document\n var boundingRect = targ.getBoundingClientRect();\n var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n\n return { x: x, y: y };\n};\n\n/*\n * return a copy of an object with only non-object keys\n * we need this to avoid circular references\n * http://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n return Object.keys(original).reduce(function (obj, key) {\n if (typeof original[key] !== 'object') {\n obj[key] = original[key];\n }\n return obj;\n }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n var canvas_pos = mpl.findpos(event);\n\n if (name === 'button_press') {\n this.canvas.focus();\n this.canvas_div.focus();\n }\n\n var x = canvas_pos.x * this.ratio;\n var y = canvas_pos.y * this.ratio;\n\n this.send_message(name, {\n x: x,\n y: y,\n button: event.button,\n step: event.step,\n guiEvent: simpleKeys(event),\n });\n\n /* This prevents the web browser from automatically changing to\n * the text insertion cursor when the button is pressed. We want\n * to control all of the cursor setting manually through the\n * 'cursor' event from matplotlib */\n event.preventDefault();\n return false;\n};\n\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n // Prevent repeat events\n if (name === 'key_press') {\n if (event.key === this._key) {\n return;\n } else {\n this._key = event.key;\n }\n }\n if (name === 'key_release') {\n this._key = null;\n }\n\n var value = '';\n if (event.ctrlKey && event.key !== 'Control') {\n value += 'ctrl+';\n }\n else if (event.altKey && event.key !== 'Alt') {\n value += 'alt+';\n }\n else if (event.shiftKey && event.key !== 'Shift') {\n value += 'shift+';\n }\n\n value += 'k' + event.key;\n\n this._key_event_extra(event, name);\n\n this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n return false;\n};\n\nmpl.figure.prototype.toolbar_button_onclick = function (name) {\n if (name === 'download') {\n this.handle_save(this, null);\n } else {\n this.send_message('toolbar_button', { name: name });\n }\n};\n\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar comm_websocket_adapter = function (comm) {\n // Create a \"websocket\"-like object which calls the given IPython comm\n // object with the appropriate methods. Currently this is a non binary\n // socket, so there is still some room for performance tuning.\n var ws = {};\n\n ws.binaryType = comm.kernel.ws.binaryType;\n ws.readyState = comm.kernel.ws.readyState;\n function updateReadyState(_event) {\n if (comm.kernel.ws) {\n ws.readyState = comm.kernel.ws.readyState;\n } else {\n ws.readyState = 3; // Closed state.\n }\n }\n comm.kernel.ws.addEventListener('open', updateReadyState);\n comm.kernel.ws.addEventListener('close', updateReadyState);\n comm.kernel.ws.addEventListener('error', updateReadyState);\n\n ws.close = function () {\n comm.close();\n };\n ws.send = function (m) {\n //console.log('sending', m);\n comm.send(m);\n };\n // Register the callback with on_msg.\n comm.on_msg(function (msg) {\n //console.log('receiving', msg['content']['data'], msg);\n var data = msg['content']['data'];\n if (data['blob'] !== undefined) {\n data = {\n data: new Blob(msg['buffers'], { type: data['blob'] }),\n };\n }\n // Pass the mpl event to the overridden (by mpl) onmessage function.\n ws.onmessage(data);\n });\n return ws;\n};\n\nmpl.mpl_figure_comm = function (comm, msg) {\n // This is the function which gets called when the mpl process\n // starts-up an IPython Comm through the \"matplotlib\" channel.\n\n var id = msg.content.data.id;\n // Get hold of the div created by the display call when the Comm\n // socket was opened in Python.\n var element = document.getElementById(id);\n var ws_proxy = comm_websocket_adapter(comm);\n\n function ondownload(figure, _format) {\n window.open(figure.canvas.toDataURL());\n }\n\n var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n\n // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n // web socket which is closed, not our websocket->open comm proxy.\n ws_proxy.onopen();\n\n fig.parent_element = element;\n fig.cell_info = mpl.find_output_cell(\"
\");\n if (!fig.cell_info) {\n console.error('Failed to find cell for figure', id, fig);\n return;\n }\n fig.cell_info[0].output_area.element.on(\n 'cleared',\n { fig: fig },\n fig._remove_fig_handler\n );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n var width = fig.canvas.width / fig.ratio;\n fig.cell_info[0].output_area.element.off(\n 'cleared',\n fig._remove_fig_handler\n );\n fig.resizeObserverInstance.unobserve(fig.canvas_div);\n\n // Update the output cell to use the data from the current canvas.\n fig.push_to_output();\n var dataURL = fig.canvas.toDataURL();\n // Re-enable the keyboard manager in IPython - without this line, in FF,\n // the notebook keyboard shortcuts fail.\n IPython.keyboard_manager.enable();\n fig.parent_element.innerHTML =\n '';\n fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n fig.send_message('closing', msg);\n // fig.ws.close()\n};\n\nmpl.figure.prototype.push_to_output = function (_remove_interactive) {\n // Turn the data on the canvas into data in the output cell.\n var width = this.canvas.width / this.ratio;\n var dataURL = this.canvas.toDataURL();\n this.cell_info[1]['text/html'] =\n '';\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n // Tell IPython that the notebook contents must change.\n IPython.notebook.set_dirty(true);\n this.send_message('ack', {});\n var fig = this;\n // Wait a second, then push the new image to the DOM so\n // that it is saved nicely (might be nice to debounce this).\n setTimeout(function () {\n fig.push_to_output();\n }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'btn-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n var button;\n for (var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n continue;\n }\n\n button = fig.buttons[name] = document.createElement('button');\n button.classList = 'btn btn-default';\n button.href = '#';\n button.title = name;\n button.innerHTML = '';\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n // Add the status bar.\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message pull-right';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n\n // Add the close button to the window.\n var buttongrp = document.createElement('div');\n buttongrp.classList = 'btn-group inline pull-right';\n button = document.createElement('button');\n button.classList = 'btn btn-mini btn-primary';\n button.href = '#';\n button.title = 'Stop Interaction';\n button.innerHTML = '';\n button.addEventListener('click', function (_evt) {\n fig.handle_close(fig, {});\n });\n button.addEventListener(\n 'mouseover',\n on_mouseover_closure('Stop Interaction')\n );\n buttongrp.appendChild(button);\n var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n var fig = event.data.fig;\n if (event.target !== this) {\n // Ignore bubbled events from children.\n return;\n }\n fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n // this is important to make the div 'focusable\n el.setAttribute('tabindex', 0);\n // reach out to IPython and tell the keyboard manager to turn it's self\n // off when our div gets focus\n\n // location in version 3\n if (IPython.notebook.keyboard_manager) {\n IPython.notebook.keyboard_manager.register_events(el);\n } else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n var manager = IPython.notebook.keyboard_manager;\n if (!manager) {\n manager = IPython.keyboard_manager;\n }\n\n // Check for shift+enter\n if (event.shiftKey && event.which === 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n fig.ondownload(fig, null);\n};\n\nmpl.find_output_cell = function (html_output) {\n // Return the cell and output element which can be found *uniquely* in the notebook.\n // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n // IPython event is triggered only after the cells have been serialised, which for\n // our purposes (turning an active figure into a static one), is too late.\n var cells = IPython.notebook.get_cells();\n var ncells = cells.length;\n for (var i = 0; i < ncells; i++) {\n var cell = cells[i];\n if (cell.cell_type === 'code') {\n for (var j = 0; j < cell.output_area.outputs.length; j++) {\n var data = cell.output_area.outputs[j];\n if (data.data) {\n // IPython >= 3 moved mimebundle to data attribute of output\n data = data.data;\n }\n if (data['text/html'] === html_output) {\n return [cell, data, j];\n }\n }\n }\n }\n};\n\n// Register the function which deals with the matplotlib target/channel.\n// The kernel may be null if the page has been refreshed.\nif (IPython.notebook.kernel !== null) {\n IPython.notebook.kernel.comm_manager.register_target(\n 'matplotlib',\n mpl.mpl_figure_comm\n );\n}\n", + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], "text/plain": [ "" ] @@ -924,7 +1921,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -965,21 +1962,967 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "motor_dashboard.initialize()" - ] - }, - { - "cell_type": "code", - "execution_count": 51, + "execution_count": 11, "metadata": {}, "outputs": [ { "data": { - "application/javascript": "/* Put everything inside the global mpl namespace */\n/* global mpl */\nwindow.mpl = {};\n\nmpl.get_websocket_type = function () {\n if (typeof WebSocket !== 'undefined') {\n return WebSocket;\n } else if (typeof MozWebSocket !== 'undefined') {\n return MozWebSocket;\n } else {\n alert(\n 'Your browser does not have WebSocket support. ' +\n 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n 'Firefox 4 and 5 are also supported but you ' +\n 'have to enable WebSockets in about:config.'\n );\n }\n};\n\nmpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n this.id = figure_id;\n\n this.ws = websocket;\n\n this.supports_binary = this.ws.binaryType !== undefined;\n\n if (!this.supports_binary) {\n var warnings = document.getElementById('mpl-warnings');\n if (warnings) {\n warnings.style.display = 'block';\n warnings.textContent =\n 'This browser does not support binary websocket messages. ' +\n 'Performance may be slow.';\n }\n }\n\n this.imageObj = new Image();\n\n this.context = undefined;\n this.message = undefined;\n this.canvas = undefined;\n this.rubberband_canvas = undefined;\n this.rubberband_context = undefined;\n this.format_dropdown = undefined;\n\n this.image_mode = 'full';\n\n this.root = document.createElement('div');\n this.root.setAttribute('style', 'display: inline-block');\n this._root_extra_style(this.root);\n\n parent_element.appendChild(this.root);\n\n this._init_header(this);\n this._init_canvas(this);\n this._init_toolbar(this);\n\n var fig = this;\n\n this.waiting = false;\n\n this.ws.onopen = function () {\n fig.send_message('supports_binary', { value: fig.supports_binary });\n fig.send_message('send_image_mode', {});\n if (fig.ratio !== 1) {\n fig.send_message('set_dpi_ratio', { dpi_ratio: fig.ratio });\n }\n fig.send_message('refresh', {});\n };\n\n this.imageObj.onload = function () {\n if (fig.image_mode === 'full') {\n // Full images could contain transparency (where diff images\n // almost always do), so we need to clear the canvas so that\n // there is no ghosting.\n fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n }\n fig.context.drawImage(fig.imageObj, 0, 0);\n };\n\n this.imageObj.onunload = function () {\n fig.ws.close();\n };\n\n this.ws.onmessage = this._make_on_message_function(this);\n\n this.ondownload = ondownload;\n};\n\nmpl.figure.prototype._init_header = function () {\n var titlebar = document.createElement('div');\n titlebar.classList =\n 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n var titletext = document.createElement('div');\n titletext.classList = 'ui-dialog-title';\n titletext.setAttribute(\n 'style',\n 'width: 100%; text-align: center; padding: 3px;'\n );\n titlebar.appendChild(titletext);\n this.root.appendChild(titlebar);\n this.header = titletext;\n};\n\nmpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n\nmpl.figure.prototype._init_canvas = function () {\n var fig = this;\n\n var canvas_div = (this.canvas_div = document.createElement('div'));\n canvas_div.setAttribute(\n 'style',\n 'border: 1px solid #ddd;' +\n 'box-sizing: content-box;' +\n 'clear: both;' +\n 'min-height: 1px;' +\n 'min-width: 1px;' +\n 'outline: 0;' +\n 'overflow: hidden;' +\n 'position: relative;' +\n 'resize: both;'\n );\n\n function on_keyboard_event_closure(name) {\n return function (event) {\n return fig.key_event(event, name);\n };\n }\n\n canvas_div.addEventListener(\n 'keydown',\n on_keyboard_event_closure('key_press')\n );\n canvas_div.addEventListener(\n 'keyup',\n on_keyboard_event_closure('key_release')\n );\n\n this._canvas_extra_style(canvas_div);\n this.root.appendChild(canvas_div);\n\n var canvas = (this.canvas = document.createElement('canvas'));\n canvas.classList.add('mpl-canvas');\n canvas.setAttribute('style', 'box-sizing: content-box;');\n\n this.context = canvas.getContext('2d');\n\n var backingStore =\n this.context.backingStorePixelRatio ||\n this.context.webkitBackingStorePixelRatio ||\n this.context.mozBackingStorePixelRatio ||\n this.context.msBackingStorePixelRatio ||\n this.context.oBackingStorePixelRatio ||\n this.context.backingStorePixelRatio ||\n 1;\n\n this.ratio = (window.devicePixelRatio || 1) / backingStore;\n\n var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n 'canvas'\n ));\n rubberband_canvas.setAttribute(\n 'style',\n 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n );\n\n // Apply a ponyfill if ResizeObserver is not implemented by browser.\n if (this.ResizeObserver === undefined) {\n if (window.ResizeObserver !== undefined) {\n this.ResizeObserver = window.ResizeObserver;\n } else {\n var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n this.ResizeObserver = obs.ResizeObserver;\n }\n }\n\n this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n var nentries = entries.length;\n for (var i = 0; i < nentries; i++) {\n var entry = entries[i];\n var width, height;\n if (entry.contentBoxSize) {\n if (entry.contentBoxSize instanceof Array) {\n // Chrome 84 implements new version of spec.\n width = entry.contentBoxSize[0].inlineSize;\n height = entry.contentBoxSize[0].blockSize;\n } else {\n // Firefox implements old version of spec.\n width = entry.contentBoxSize.inlineSize;\n height = entry.contentBoxSize.blockSize;\n }\n } else {\n // Chrome <84 implements even older version of spec.\n width = entry.contentRect.width;\n height = entry.contentRect.height;\n }\n\n // Keep the size of the canvas and rubber band canvas in sync with\n // the canvas container.\n if (entry.devicePixelContentBoxSize) {\n // Chrome 84 implements new version of spec.\n canvas.setAttribute(\n 'width',\n entry.devicePixelContentBoxSize[0].inlineSize\n );\n canvas.setAttribute(\n 'height',\n entry.devicePixelContentBoxSize[0].blockSize\n );\n } else {\n canvas.setAttribute('width', width * fig.ratio);\n canvas.setAttribute('height', height * fig.ratio);\n }\n canvas.setAttribute(\n 'style',\n 'width: ' + width + 'px; height: ' + height + 'px;'\n );\n\n rubberband_canvas.setAttribute('width', width);\n rubberband_canvas.setAttribute('height', height);\n\n // And update the size in Python. We ignore the initial 0/0 size\n // that occurs as the element is placed into the DOM, which should\n // otherwise not happen due to the minimum size styling.\n if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n fig.request_resize(width, height);\n }\n }\n });\n this.resizeObserverInstance.observe(canvas_div);\n\n function on_mouse_event_closure(name) {\n return function (event) {\n return fig.mouse_event(event, name);\n };\n }\n\n rubberband_canvas.addEventListener(\n 'mousedown',\n on_mouse_event_closure('button_press')\n );\n rubberband_canvas.addEventListener(\n 'mouseup',\n on_mouse_event_closure('button_release')\n );\n rubberband_canvas.addEventListener(\n 'dblclick',\n on_mouse_event_closure('dblclick')\n );\n // Throttle sequential mouse events to 1 every 20ms.\n rubberband_canvas.addEventListener(\n 'mousemove',\n on_mouse_event_closure('motion_notify')\n );\n\n rubberband_canvas.addEventListener(\n 'mouseenter',\n on_mouse_event_closure('figure_enter')\n );\n rubberband_canvas.addEventListener(\n 'mouseleave',\n on_mouse_event_closure('figure_leave')\n );\n\n canvas_div.addEventListener('wheel', function (event) {\n if (event.deltaY < 0) {\n event.step = 1;\n } else {\n event.step = -1;\n }\n on_mouse_event_closure('scroll')(event);\n });\n\n canvas_div.appendChild(canvas);\n canvas_div.appendChild(rubberband_canvas);\n\n this.rubberband_context = rubberband_canvas.getContext('2d');\n this.rubberband_context.strokeStyle = '#000000';\n\n this._resize_canvas = function (width, height, forward) {\n if (forward) {\n canvas_div.style.width = width + 'px';\n canvas_div.style.height = height + 'px';\n }\n };\n\n // Disable right mouse context menu.\n this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n event.preventDefault();\n return false;\n });\n\n function set_focus() {\n canvas.focus();\n canvas_div.focus();\n }\n\n window.setTimeout(set_focus, 100);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'mpl-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n for (var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'mpl-button-group';\n continue;\n }\n\n var button = (fig.buttons[name] = document.createElement('button'));\n button.classList = 'mpl-widget';\n button.setAttribute('role', 'button');\n button.setAttribute('aria-disabled', 'false');\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n\n var icon_img = document.createElement('img');\n icon_img.src = '_images/' + image + '.png';\n icon_img.srcset = '_images/' + image + '_large.png 2x';\n icon_img.alt = tooltip;\n button.appendChild(icon_img);\n\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n var fmt_picker = document.createElement('select');\n fmt_picker.classList = 'mpl-widget';\n toolbar.appendChild(fmt_picker);\n this.format_dropdown = fmt_picker;\n\n for (var ind in mpl.extensions) {\n var fmt = mpl.extensions[ind];\n var option = document.createElement('option');\n option.selected = fmt === mpl.default_extension;\n option.innerHTML = fmt;\n fmt_picker.appendChild(option);\n }\n\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n};\n\nmpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n // which will in turn request a refresh of the image.\n this.send_message('resize', { width: x_pixels, height: y_pixels });\n};\n\nmpl.figure.prototype.send_message = function (type, properties) {\n properties['type'] = type;\n properties['figure_id'] = this.id;\n this.ws.send(JSON.stringify(properties));\n};\n\nmpl.figure.prototype.send_draw_message = function () {\n if (!this.waiting) {\n this.waiting = true;\n this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n var format_dropdown = fig.format_dropdown;\n var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n fig.ondownload(fig, format);\n};\n\nmpl.figure.prototype.handle_resize = function (fig, msg) {\n var size = msg['size'];\n if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n fig._resize_canvas(size[0], size[1], msg['forward']);\n fig.send_message('refresh', {});\n }\n};\n\nmpl.figure.prototype.handle_rubberband = function (fig, msg) {\n var x0 = msg['x0'] / fig.ratio;\n var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n var x1 = msg['x1'] / fig.ratio;\n var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n x0 = Math.floor(x0) + 0.5;\n y0 = Math.floor(y0) + 0.5;\n x1 = Math.floor(x1) + 0.5;\n y1 = Math.floor(y1) + 0.5;\n var min_x = Math.min(x0, x1);\n var min_y = Math.min(y0, y1);\n var width = Math.abs(x1 - x0);\n var height = Math.abs(y1 - y0);\n\n fig.rubberband_context.clearRect(\n 0,\n 0,\n fig.canvas.width / fig.ratio,\n fig.canvas.height / fig.ratio\n );\n\n fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n};\n\nmpl.figure.prototype.handle_figure_label = function (fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n};\n\nmpl.figure.prototype.handle_cursor = function (fig, msg) {\n var cursor = msg['cursor'];\n switch (cursor) {\n case 0:\n cursor = 'pointer';\n break;\n case 1:\n cursor = 'default';\n break;\n case 2:\n cursor = 'crosshair';\n break;\n case 3:\n cursor = 'move';\n break;\n }\n fig.rubberband_canvas.style.cursor = cursor;\n};\n\nmpl.figure.prototype.handle_message = function (fig, msg) {\n fig.message.textContent = msg['message'];\n};\n\nmpl.figure.prototype.handle_draw = function (fig, _msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n};\n\nmpl.figure.prototype.handle_image_mode = function (fig, msg) {\n fig.image_mode = msg['mode'];\n};\n\nmpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n for (var key in msg) {\n if (!(key in fig.buttons)) {\n continue;\n }\n fig.buttons[key].disabled = !msg[key];\n fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n }\n};\n\nmpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n if (msg['mode'] === 'PAN') {\n fig.buttons['Pan'].classList.add('active');\n fig.buttons['Zoom'].classList.remove('active');\n } else if (msg['mode'] === 'ZOOM') {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.add('active');\n } else {\n fig.buttons['Pan'].classList.remove('active');\n fig.buttons['Zoom'].classList.remove('active');\n }\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n // Called whenever the canvas gets updated.\n this.send_message('ack', {});\n};\n\n// A function to construct a web socket function for onmessage handling.\n// Called in the figure constructor.\nmpl.figure.prototype._make_on_message_function = function (fig) {\n return function socket_on_message(evt) {\n if (evt.data instanceof Blob) {\n var img = evt.data;\n if (img.type !== 'image/png') {\n /* FIXME: We get \"Resource interpreted as Image but\n * transferred with MIME type text/plain:\" errors on\n * Chrome. But how to set the MIME type? It doesn't seem\n * to be part of the websocket stream */\n img.type = 'image/png';\n }\n\n /* Free the memory for the previous frames */\n if (fig.imageObj.src) {\n (window.URL || window.webkitURL).revokeObjectURL(\n fig.imageObj.src\n );\n }\n\n fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n img\n );\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n } else if (\n typeof evt.data === 'string' &&\n evt.data.slice(0, 21) === 'data:image/png;base64'\n ) {\n fig.imageObj.src = evt.data;\n fig.updated_canvas_event();\n fig.waiting = false;\n return;\n }\n\n var msg = JSON.parse(evt.data);\n var msg_type = msg['type'];\n\n // Call the \"handle_{type}\" callback, which takes\n // the figure and JSON message as its only arguments.\n try {\n var callback = fig['handle_' + msg_type];\n } catch (e) {\n console.log(\n \"No handler for the '\" + msg_type + \"' message type: \",\n msg\n );\n return;\n }\n\n if (callback) {\n try {\n // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n callback(fig, msg);\n } catch (e) {\n console.log(\n \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n e,\n e.stack,\n msg\n );\n }\n }\n };\n};\n\n// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\nmpl.findpos = function (e) {\n //this section is from http://www.quirksmode.org/js/events_properties.html\n var targ;\n if (!e) {\n e = window.event;\n }\n if (e.target) {\n targ = e.target;\n } else if (e.srcElement) {\n targ = e.srcElement;\n }\n if (targ.nodeType === 3) {\n // defeat Safari bug\n targ = targ.parentNode;\n }\n\n // pageX,Y are the mouse positions relative to the document\n var boundingRect = targ.getBoundingClientRect();\n var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n\n return { x: x, y: y };\n};\n\n/*\n * return a copy of an object with only non-object keys\n * we need this to avoid circular references\n * http://stackoverflow.com/a/24161582/3208463\n */\nfunction simpleKeys(original) {\n return Object.keys(original).reduce(function (obj, key) {\n if (typeof original[key] !== 'object') {\n obj[key] = original[key];\n }\n return obj;\n }, {});\n}\n\nmpl.figure.prototype.mouse_event = function (event, name) {\n var canvas_pos = mpl.findpos(event);\n\n if (name === 'button_press') {\n this.canvas.focus();\n this.canvas_div.focus();\n }\n\n var x = canvas_pos.x * this.ratio;\n var y = canvas_pos.y * this.ratio;\n\n this.send_message(name, {\n x: x,\n y: y,\n button: event.button,\n step: event.step,\n guiEvent: simpleKeys(event),\n });\n\n /* This prevents the web browser from automatically changing to\n * the text insertion cursor when the button is pressed. We want\n * to control all of the cursor setting manually through the\n * 'cursor' event from matplotlib */\n event.preventDefault();\n return false;\n};\n\nmpl.figure.prototype._key_event_extra = function (_event, _name) {\n // Handle any extra behaviour associated with a key event\n};\n\nmpl.figure.prototype.key_event = function (event, name) {\n // Prevent repeat events\n if (name === 'key_press') {\n if (event.key === this._key) {\n return;\n } else {\n this._key = event.key;\n }\n }\n if (name === 'key_release') {\n this._key = null;\n }\n\n var value = '';\n if (event.ctrlKey && event.key !== 'Control') {\n value += 'ctrl+';\n }\n else if (event.altKey && event.key !== 'Alt') {\n value += 'alt+';\n }\n else if (event.shiftKey && event.key !== 'Shift') {\n value += 'shift+';\n }\n\n value += 'k' + event.key;\n\n this._key_event_extra(event, name);\n\n this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n return false;\n};\n\nmpl.figure.prototype.toolbar_button_onclick = function (name) {\n if (name === 'download') {\n this.handle_save(this, null);\n } else {\n this.send_message('toolbar_button', { name: name });\n }\n};\n\nmpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n this.message.textContent = tooltip;\n};\n\n///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n// prettier-ignore\nvar _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\nmpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n\nmpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n\nmpl.default_extension = \"png\";/* global mpl */\n\nvar comm_websocket_adapter = function (comm) {\n // Create a \"websocket\"-like object which calls the given IPython comm\n // object with the appropriate methods. Currently this is a non binary\n // socket, so there is still some room for performance tuning.\n var ws = {};\n\n ws.binaryType = comm.kernel.ws.binaryType;\n ws.readyState = comm.kernel.ws.readyState;\n function updateReadyState(_event) {\n if (comm.kernel.ws) {\n ws.readyState = comm.kernel.ws.readyState;\n } else {\n ws.readyState = 3; // Closed state.\n }\n }\n comm.kernel.ws.addEventListener('open', updateReadyState);\n comm.kernel.ws.addEventListener('close', updateReadyState);\n comm.kernel.ws.addEventListener('error', updateReadyState);\n\n ws.close = function () {\n comm.close();\n };\n ws.send = function (m) {\n //console.log('sending', m);\n comm.send(m);\n };\n // Register the callback with on_msg.\n comm.on_msg(function (msg) {\n //console.log('receiving', msg['content']['data'], msg);\n var data = msg['content']['data'];\n if (data['blob'] !== undefined) {\n data = {\n data: new Blob(msg['buffers'], { type: data['blob'] }),\n };\n }\n // Pass the mpl event to the overridden (by mpl) onmessage function.\n ws.onmessage(data);\n });\n return ws;\n};\n\nmpl.mpl_figure_comm = function (comm, msg) {\n // This is the function which gets called when the mpl process\n // starts-up an IPython Comm through the \"matplotlib\" channel.\n\n var id = msg.content.data.id;\n // Get hold of the div created by the display call when the Comm\n // socket was opened in Python.\n var element = document.getElementById(id);\n var ws_proxy = comm_websocket_adapter(comm);\n\n function ondownload(figure, _format) {\n window.open(figure.canvas.toDataURL());\n }\n\n var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n\n // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n // web socket which is closed, not our websocket->open comm proxy.\n ws_proxy.onopen();\n\n fig.parent_element = element;\n fig.cell_info = mpl.find_output_cell(\"
\");\n if (!fig.cell_info) {\n console.error('Failed to find cell for figure', id, fig);\n return;\n }\n fig.cell_info[0].output_area.element.on(\n 'cleared',\n { fig: fig },\n fig._remove_fig_handler\n );\n};\n\nmpl.figure.prototype.handle_close = function (fig, msg) {\n var width = fig.canvas.width / fig.ratio;\n fig.cell_info[0].output_area.element.off(\n 'cleared',\n fig._remove_fig_handler\n );\n fig.resizeObserverInstance.unobserve(fig.canvas_div);\n\n // Update the output cell to use the data from the current canvas.\n fig.push_to_output();\n var dataURL = fig.canvas.toDataURL();\n // Re-enable the keyboard manager in IPython - without this line, in FF,\n // the notebook keyboard shortcuts fail.\n IPython.keyboard_manager.enable();\n fig.parent_element.innerHTML =\n '';\n fig.close_ws(fig, msg);\n};\n\nmpl.figure.prototype.close_ws = function (fig, msg) {\n fig.send_message('closing', msg);\n // fig.ws.close()\n};\n\nmpl.figure.prototype.push_to_output = function (_remove_interactive) {\n // Turn the data on the canvas into data in the output cell.\n var width = this.canvas.width / this.ratio;\n var dataURL = this.canvas.toDataURL();\n this.cell_info[1]['text/html'] =\n '';\n};\n\nmpl.figure.prototype.updated_canvas_event = function () {\n // Tell IPython that the notebook contents must change.\n IPython.notebook.set_dirty(true);\n this.send_message('ack', {});\n var fig = this;\n // Wait a second, then push the new image to the DOM so\n // that it is saved nicely (might be nice to debounce this).\n setTimeout(function () {\n fig.push_to_output();\n }, 1000);\n};\n\nmpl.figure.prototype._init_toolbar = function () {\n var fig = this;\n\n var toolbar = document.createElement('div');\n toolbar.classList = 'btn-toolbar';\n this.root.appendChild(toolbar);\n\n function on_click_closure(name) {\n return function (_event) {\n return fig.toolbar_button_onclick(name);\n };\n }\n\n function on_mouseover_closure(tooltip) {\n return function (event) {\n if (!event.currentTarget.disabled) {\n return fig.toolbar_button_onmouseover(tooltip);\n }\n };\n }\n\n fig.buttons = {};\n var buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n var button;\n for (var toolbar_ind in mpl.toolbar_items) {\n var name = mpl.toolbar_items[toolbar_ind][0];\n var tooltip = mpl.toolbar_items[toolbar_ind][1];\n var image = mpl.toolbar_items[toolbar_ind][2];\n var method_name = mpl.toolbar_items[toolbar_ind][3];\n\n if (!name) {\n /* Instead of a spacer, we start a new button group. */\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n buttonGroup = document.createElement('div');\n buttonGroup.classList = 'btn-group';\n continue;\n }\n\n button = fig.buttons[name] = document.createElement('button');\n button.classList = 'btn btn-default';\n button.href = '#';\n button.title = name;\n button.innerHTML = '';\n button.addEventListener('click', on_click_closure(method_name));\n button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n buttonGroup.appendChild(button);\n }\n\n if (buttonGroup.hasChildNodes()) {\n toolbar.appendChild(buttonGroup);\n }\n\n // Add the status bar.\n var status_bar = document.createElement('span');\n status_bar.classList = 'mpl-message pull-right';\n toolbar.appendChild(status_bar);\n this.message = status_bar;\n\n // Add the close button to the window.\n var buttongrp = document.createElement('div');\n buttongrp.classList = 'btn-group inline pull-right';\n button = document.createElement('button');\n button.classList = 'btn btn-mini btn-primary';\n button.href = '#';\n button.title = 'Stop Interaction';\n button.innerHTML = '';\n button.addEventListener('click', function (_evt) {\n fig.handle_close(fig, {});\n });\n button.addEventListener(\n 'mouseover',\n on_mouseover_closure('Stop Interaction')\n );\n buttongrp.appendChild(button);\n var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n titlebar.insertBefore(buttongrp, titlebar.firstChild);\n};\n\nmpl.figure.prototype._remove_fig_handler = function (event) {\n var fig = event.data.fig;\n if (event.target !== this) {\n // Ignore bubbled events from children.\n return;\n }\n fig.close_ws(fig, {});\n};\n\nmpl.figure.prototype._root_extra_style = function (el) {\n el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n};\n\nmpl.figure.prototype._canvas_extra_style = function (el) {\n // this is important to make the div 'focusable\n el.setAttribute('tabindex', 0);\n // reach out to IPython and tell the keyboard manager to turn it's self\n // off when our div gets focus\n\n // location in version 3\n if (IPython.notebook.keyboard_manager) {\n IPython.notebook.keyboard_manager.register_events(el);\n } else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n};\n\nmpl.figure.prototype._key_event_extra = function (event, _name) {\n var manager = IPython.notebook.keyboard_manager;\n if (!manager) {\n manager = IPython.keyboard_manager;\n }\n\n // Check for shift+enter\n if (event.shiftKey && event.which === 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n};\n\nmpl.figure.prototype.handle_save = function (fig, _msg) {\n fig.ondownload(fig, null);\n};\n\nmpl.find_output_cell = function (html_output) {\n // Return the cell and output element which can be found *uniquely* in the notebook.\n // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n // IPython event is triggered only after the cells have been serialised, which for\n // our purposes (turning an active figure into a static one), is too late.\n var cells = IPython.notebook.get_cells();\n var ncells = cells.length;\n for (var i = 0; i < ncells; i++) {\n var cell = cells[i];\n if (cell.cell_type === 'code') {\n for (var j = 0; j < cell.output_area.outputs.length; j++) {\n var data = cell.output_area.outputs[j];\n if (data.data) {\n // IPython >= 3 moved mimebundle to data attribute of output\n data = data.data;\n }\n if (data['text/html'] === html_output) {\n return [cell, data, j];\n }\n }\n }\n }\n};\n\n// Register the function which deals with the matplotlib target/channel.\n// The kernel may be null if the page has been refreshed.\nif (IPython.notebook.kernel !== null) {\n IPython.notebook.kernel.comm_manager.register_target(\n 'matplotlib',\n mpl.mpl_figure_comm\n );\n}\n", + "application/javascript": [ + "/* Put everything inside the global mpl namespace */\n", + "/* global mpl */\n", + "window.mpl = {};\n", + "\n", + "mpl.get_websocket_type = function () {\n", + " if (typeof WebSocket !== 'undefined') {\n", + " return WebSocket;\n", + " } else if (typeof MozWebSocket !== 'undefined') {\n", + " return MozWebSocket;\n", + " } else {\n", + " alert(\n", + " 'Your browser does not have WebSocket support. ' +\n", + " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", + " 'Firefox 4 and 5 are also supported but you ' +\n", + " 'have to enable WebSockets in about:config.'\n", + " );\n", + " }\n", + "};\n", + "\n", + "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", + " this.id = figure_id;\n", + "\n", + " this.ws = websocket;\n", + "\n", + " this.supports_binary = this.ws.binaryType !== undefined;\n", + "\n", + " if (!this.supports_binary) {\n", + " var warnings = document.getElementById('mpl-warnings');\n", + " if (warnings) {\n", + " warnings.style.display = 'block';\n", + " warnings.textContent =\n", + " 'This browser does not support binary websocket messages. ' +\n", + " 'Performance may be slow.';\n", + " }\n", + " }\n", + "\n", + " this.imageObj = new Image();\n", + "\n", + " this.context = undefined;\n", + " this.message = undefined;\n", + " this.canvas = undefined;\n", + " this.rubberband_canvas = undefined;\n", + " this.rubberband_context = undefined;\n", + " this.format_dropdown = undefined;\n", + "\n", + " this.image_mode = 'full';\n", + "\n", + " this.root = document.createElement('div');\n", + " this.root.setAttribute('style', 'display: inline-block');\n", + " this._root_extra_style(this.root);\n", + "\n", + " parent_element.appendChild(this.root);\n", + "\n", + " this._init_header(this);\n", + " this._init_canvas(this);\n", + " this._init_toolbar(this);\n", + "\n", + " var fig = this;\n", + "\n", + " this.waiting = false;\n", + "\n", + " this.ws.onopen = function () {\n", + " fig.send_message('supports_binary', { value: fig.supports_binary });\n", + " fig.send_message('send_image_mode', {});\n", + " if (fig.ratio !== 1) {\n", + " fig.send_message('set_device_pixel_ratio', {\n", + " device_pixel_ratio: fig.ratio,\n", + " });\n", + " }\n", + " fig.send_message('refresh', {});\n", + " };\n", + "\n", + " this.imageObj.onload = function () {\n", + " if (fig.image_mode === 'full') {\n", + " // Full images could contain transparency (where diff images\n", + " // almost always do), so we need to clear the canvas so that\n", + " // there is no ghosting.\n", + " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", + " }\n", + " fig.context.drawImage(fig.imageObj, 0, 0);\n", + " };\n", + "\n", + " this.imageObj.onunload = function () {\n", + " fig.ws.close();\n", + " };\n", + "\n", + " this.ws.onmessage = this._make_on_message_function(this);\n", + "\n", + " this.ondownload = ondownload;\n", + "};\n", + "\n", + "mpl.figure.prototype._init_header = function () {\n", + " var titlebar = document.createElement('div');\n", + " titlebar.classList =\n", + " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", + " var titletext = document.createElement('div');\n", + " titletext.classList = 'ui-dialog-title';\n", + " titletext.setAttribute(\n", + " 'style',\n", + " 'width: 100%; text-align: center; padding: 3px;'\n", + " );\n", + " titlebar.appendChild(titletext);\n", + " this.root.appendChild(titlebar);\n", + " this.header = titletext;\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", + "\n", + "mpl.figure.prototype._init_canvas = function () {\n", + " var fig = this;\n", + "\n", + " var canvas_div = (this.canvas_div = document.createElement('div'));\n", + " canvas_div.setAttribute(\n", + " 'style',\n", + " 'border: 1px solid #ddd;' +\n", + " 'box-sizing: content-box;' +\n", + " 'clear: both;' +\n", + " 'min-height: 1px;' +\n", + " 'min-width: 1px;' +\n", + " 'outline: 0;' +\n", + " 'overflow: hidden;' +\n", + " 'position: relative;' +\n", + " 'resize: both;'\n", + " );\n", + "\n", + " function on_keyboard_event_closure(name) {\n", + " return function (event) {\n", + " return fig.key_event(event, name);\n", + " };\n", + " }\n", + "\n", + " canvas_div.addEventListener(\n", + " 'keydown',\n", + " on_keyboard_event_closure('key_press')\n", + " );\n", + " canvas_div.addEventListener(\n", + " 'keyup',\n", + " on_keyboard_event_closure('key_release')\n", + " );\n", + "\n", + " this._canvas_extra_style(canvas_div);\n", + " this.root.appendChild(canvas_div);\n", + "\n", + " var canvas = (this.canvas = document.createElement('canvas'));\n", + " canvas.classList.add('mpl-canvas');\n", + " canvas.setAttribute('style', 'box-sizing: content-box;');\n", + "\n", + " this.context = canvas.getContext('2d');\n", + "\n", + " var backingStore =\n", + " this.context.backingStorePixelRatio ||\n", + " this.context.webkitBackingStorePixelRatio ||\n", + " this.context.mozBackingStorePixelRatio ||\n", + " this.context.msBackingStorePixelRatio ||\n", + " this.context.oBackingStorePixelRatio ||\n", + " this.context.backingStorePixelRatio ||\n", + " 1;\n", + "\n", + " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", + "\n", + " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", + " 'canvas'\n", + " ));\n", + " rubberband_canvas.setAttribute(\n", + " 'style',\n", + " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", + " );\n", + "\n", + " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", + " if (this.ResizeObserver === undefined) {\n", + " if (window.ResizeObserver !== undefined) {\n", + " this.ResizeObserver = window.ResizeObserver;\n", + " } else {\n", + " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", + " this.ResizeObserver = obs.ResizeObserver;\n", + " }\n", + " }\n", + "\n", + " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", + " var nentries = entries.length;\n", + " for (var i = 0; i < nentries; i++) {\n", + " var entry = entries[i];\n", + " var width, height;\n", + " if (entry.contentBoxSize) {\n", + " if (entry.contentBoxSize instanceof Array) {\n", + " // Chrome 84 implements new version of spec.\n", + " width = entry.contentBoxSize[0].inlineSize;\n", + " height = entry.contentBoxSize[0].blockSize;\n", + " } else {\n", + " // Firefox implements old version of spec.\n", + " width = entry.contentBoxSize.inlineSize;\n", + " height = entry.contentBoxSize.blockSize;\n", + " }\n", + " } else {\n", + " // Chrome <84 implements even older version of spec.\n", + " width = entry.contentRect.width;\n", + " height = entry.contentRect.height;\n", + " }\n", + "\n", + " // Keep the size of the canvas and rubber band canvas in sync with\n", + " // the canvas container.\n", + " if (entry.devicePixelContentBoxSize) {\n", + " // Chrome 84 implements new version of spec.\n", + " canvas.setAttribute(\n", + " 'width',\n", + " entry.devicePixelContentBoxSize[0].inlineSize\n", + " );\n", + " canvas.setAttribute(\n", + " 'height',\n", + " entry.devicePixelContentBoxSize[0].blockSize\n", + " );\n", + " } else {\n", + " canvas.setAttribute('width', width * fig.ratio);\n", + " canvas.setAttribute('height', height * fig.ratio);\n", + " }\n", + " canvas.setAttribute(\n", + " 'style',\n", + " 'width: ' + width + 'px; height: ' + height + 'px;'\n", + " );\n", + "\n", + " rubberband_canvas.setAttribute('width', width);\n", + " rubberband_canvas.setAttribute('height', height);\n", + "\n", + " // And update the size in Python. We ignore the initial 0/0 size\n", + " // that occurs as the element is placed into the DOM, which should\n", + " // otherwise not happen due to the minimum size styling.\n", + " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", + " fig.request_resize(width, height);\n", + " }\n", + " }\n", + " });\n", + " this.resizeObserverInstance.observe(canvas_div);\n", + "\n", + " function on_mouse_event_closure(name) {\n", + " return function (event) {\n", + " return fig.mouse_event(event, name);\n", + " };\n", + " }\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mousedown',\n", + " on_mouse_event_closure('button_press')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseup',\n", + " on_mouse_event_closure('button_release')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'dblclick',\n", + " on_mouse_event_closure('dblclick')\n", + " );\n", + " // Throttle sequential mouse events to 1 every 20ms.\n", + " rubberband_canvas.addEventListener(\n", + " 'mousemove',\n", + " on_mouse_event_closure('motion_notify')\n", + " );\n", + "\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseenter',\n", + " on_mouse_event_closure('figure_enter')\n", + " );\n", + " rubberband_canvas.addEventListener(\n", + " 'mouseleave',\n", + " on_mouse_event_closure('figure_leave')\n", + " );\n", + "\n", + " canvas_div.addEventListener('wheel', function (event) {\n", + " if (event.deltaY < 0) {\n", + " event.step = 1;\n", + " } else {\n", + " event.step = -1;\n", + " }\n", + " on_mouse_event_closure('scroll')(event);\n", + " });\n", + "\n", + " canvas_div.appendChild(canvas);\n", + " canvas_div.appendChild(rubberband_canvas);\n", + "\n", + " this.rubberband_context = rubberband_canvas.getContext('2d');\n", + " this.rubberband_context.strokeStyle = '#000000';\n", + "\n", + " this._resize_canvas = function (width, height, forward) {\n", + " if (forward) {\n", + " canvas_div.style.width = width + 'px';\n", + " canvas_div.style.height = height + 'px';\n", + " }\n", + " };\n", + "\n", + " // Disable right mouse context menu.\n", + " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", + " event.preventDefault();\n", + " return false;\n", + " });\n", + "\n", + " function set_focus() {\n", + " canvas.focus();\n", + " canvas_div.focus();\n", + " }\n", + "\n", + " window.setTimeout(set_focus, 100);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'mpl-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'mpl-button-group';\n", + " continue;\n", + " }\n", + "\n", + " var button = (fig.buttons[name] = document.createElement('button'));\n", + " button.classList = 'mpl-widget';\n", + " button.setAttribute('role', 'button');\n", + " button.setAttribute('aria-disabled', 'false');\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + "\n", + " var icon_img = document.createElement('img');\n", + " icon_img.src = '_images/' + image + '.png';\n", + " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", + " icon_img.alt = tooltip;\n", + " button.appendChild(icon_img);\n", + "\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " var fmt_picker = document.createElement('select');\n", + " fmt_picker.classList = 'mpl-widget';\n", + " toolbar.appendChild(fmt_picker);\n", + " this.format_dropdown = fmt_picker;\n", + "\n", + " for (var ind in mpl.extensions) {\n", + " var fmt = mpl.extensions[ind];\n", + " var option = document.createElement('option');\n", + " option.selected = fmt === mpl.default_extension;\n", + " option.innerHTML = fmt;\n", + " fmt_picker.appendChild(option);\n", + " }\n", + "\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "};\n", + "\n", + "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", + " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", + " // which will in turn request a refresh of the image.\n", + " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", + "};\n", + "\n", + "mpl.figure.prototype.send_message = function (type, properties) {\n", + " properties['type'] = type;\n", + " properties['figure_id'] = this.id;\n", + " this.ws.send(JSON.stringify(properties));\n", + "};\n", + "\n", + "mpl.figure.prototype.send_draw_message = function () {\n", + " if (!this.waiting) {\n", + " this.waiting = true;\n", + " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " var format_dropdown = fig.format_dropdown;\n", + " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", + " fig.ondownload(fig, format);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", + " var size = msg['size'];\n", + " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", + " fig._resize_canvas(size[0], size[1], msg['forward']);\n", + " fig.send_message('refresh', {});\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", + " var x0 = msg['x0'] / fig.ratio;\n", + " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", + " var x1 = msg['x1'] / fig.ratio;\n", + " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", + " x0 = Math.floor(x0) + 0.5;\n", + " y0 = Math.floor(y0) + 0.5;\n", + " x1 = Math.floor(x1) + 0.5;\n", + " y1 = Math.floor(y1) + 0.5;\n", + " var min_x = Math.min(x0, x1);\n", + " var min_y = Math.min(y0, y1);\n", + " var width = Math.abs(x1 - x0);\n", + " var height = Math.abs(y1 - y0);\n", + "\n", + " fig.rubberband_context.clearRect(\n", + " 0,\n", + " 0,\n", + " fig.canvas.width / fig.ratio,\n", + " fig.canvas.height / fig.ratio\n", + " );\n", + "\n", + " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", + " // Updates the figure title.\n", + " fig.header.textContent = msg['label'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", + " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_message = function (fig, msg) {\n", + " fig.message.textContent = msg['message'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", + " // Request the server to send over a new figure.\n", + " fig.send_draw_message();\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", + " fig.image_mode = msg['mode'];\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", + " for (var key in msg) {\n", + " if (!(key in fig.buttons)) {\n", + " continue;\n", + " }\n", + " fig.buttons[key].disabled = !msg[key];\n", + " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", + " if (msg['mode'] === 'PAN') {\n", + " fig.buttons['Pan'].classList.add('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " } else if (msg['mode'] === 'ZOOM') {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.add('active');\n", + " } else {\n", + " fig.buttons['Pan'].classList.remove('active');\n", + " fig.buttons['Zoom'].classList.remove('active');\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Called whenever the canvas gets updated.\n", + " this.send_message('ack', {});\n", + "};\n", + "\n", + "// A function to construct a web socket function for onmessage handling.\n", + "// Called in the figure constructor.\n", + "mpl.figure.prototype._make_on_message_function = function (fig) {\n", + " return function socket_on_message(evt) {\n", + " if (evt.data instanceof Blob) {\n", + " var img = evt.data;\n", + " if (img.type !== 'image/png') {\n", + " /* FIXME: We get \"Resource interpreted as Image but\n", + " * transferred with MIME type text/plain:\" errors on\n", + " * Chrome. But how to set the MIME type? It doesn't seem\n", + " * to be part of the websocket stream */\n", + " img.type = 'image/png';\n", + " }\n", + "\n", + " /* Free the memory for the previous frames */\n", + " if (fig.imageObj.src) {\n", + " (window.URL || window.webkitURL).revokeObjectURL(\n", + " fig.imageObj.src\n", + " );\n", + " }\n", + "\n", + " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", + " img\n", + " );\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " } else if (\n", + " typeof evt.data === 'string' &&\n", + " evt.data.slice(0, 21) === 'data:image/png;base64'\n", + " ) {\n", + " fig.imageObj.src = evt.data;\n", + " fig.updated_canvas_event();\n", + " fig.waiting = false;\n", + " return;\n", + " }\n", + "\n", + " var msg = JSON.parse(evt.data);\n", + " var msg_type = msg['type'];\n", + "\n", + " // Call the \"handle_{type}\" callback, which takes\n", + " // the figure and JSON message as its only arguments.\n", + " try {\n", + " var callback = fig['handle_' + msg_type];\n", + " } catch (e) {\n", + " console.log(\n", + " \"No handler for the '\" + msg_type + \"' message type: \",\n", + " msg\n", + " );\n", + " return;\n", + " }\n", + "\n", + " if (callback) {\n", + " try {\n", + " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", + " callback(fig, msg);\n", + " } catch (e) {\n", + " console.log(\n", + " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", + " e,\n", + " e.stack,\n", + " msg\n", + " );\n", + " }\n", + " }\n", + " };\n", + "};\n", + "\n", + "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", + "mpl.findpos = function (e) {\n", + " //this section is from http://www.quirksmode.org/js/events_properties.html\n", + " var targ;\n", + " if (!e) {\n", + " e = window.event;\n", + " }\n", + " if (e.target) {\n", + " targ = e.target;\n", + " } else if (e.srcElement) {\n", + " targ = e.srcElement;\n", + " }\n", + " if (targ.nodeType === 3) {\n", + " // defeat Safari bug\n", + " targ = targ.parentNode;\n", + " }\n", + "\n", + " // pageX,Y are the mouse positions relative to the document\n", + " var boundingRect = targ.getBoundingClientRect();\n", + " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", + " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", + "\n", + " return { x: x, y: y };\n", + "};\n", + "\n", + "/*\n", + " * return a copy of an object with only non-object keys\n", + " * we need this to avoid circular references\n", + " * https://stackoverflow.com/a/24161582/3208463\n", + " */\n", + "function simpleKeys(original) {\n", + " return Object.keys(original).reduce(function (obj, key) {\n", + " if (typeof original[key] !== 'object') {\n", + " obj[key] = original[key];\n", + " }\n", + " return obj;\n", + " }, {});\n", + "}\n", + "\n", + "mpl.figure.prototype.mouse_event = function (event, name) {\n", + " var canvas_pos = mpl.findpos(event);\n", + "\n", + " if (name === 'button_press') {\n", + " this.canvas.focus();\n", + " this.canvas_div.focus();\n", + " }\n", + "\n", + " var x = canvas_pos.x * this.ratio;\n", + " var y = canvas_pos.y * this.ratio;\n", + "\n", + " this.send_message(name, {\n", + " x: x,\n", + " y: y,\n", + " button: event.button,\n", + " step: event.step,\n", + " guiEvent: simpleKeys(event),\n", + " });\n", + "\n", + " /* This prevents the web browser from automatically changing to\n", + " * the text insertion cursor when the button is pressed. We want\n", + " * to control all of the cursor setting manually through the\n", + " * 'cursor' event from matplotlib */\n", + " event.preventDefault();\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", + " // Handle any extra behaviour associated with a key event\n", + "};\n", + "\n", + "mpl.figure.prototype.key_event = function (event, name) {\n", + " // Prevent repeat events\n", + " if (name === 'key_press') {\n", + " if (event.key === this._key) {\n", + " return;\n", + " } else {\n", + " this._key = event.key;\n", + " }\n", + " }\n", + " if (name === 'key_release') {\n", + " this._key = null;\n", + " }\n", + "\n", + " var value = '';\n", + " if (event.ctrlKey && event.key !== 'Control') {\n", + " value += 'ctrl+';\n", + " }\n", + " else if (event.altKey && event.key !== 'Alt') {\n", + " value += 'alt+';\n", + " }\n", + " else if (event.shiftKey && event.key !== 'Shift') {\n", + " value += 'shift+';\n", + " }\n", + "\n", + " value += 'k' + event.key;\n", + "\n", + " this._key_event_extra(event, name);\n", + "\n", + " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", + " return false;\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", + " if (name === 'download') {\n", + " this.handle_save(this, null);\n", + " } else {\n", + " this.send_message('toolbar_button', { name: name });\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", + " this.message.textContent = tooltip;\n", + "};\n", + "\n", + "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", + "// prettier-ignore\n", + "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", + "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", + "\n", + "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", + "\n", + "mpl.default_extension = \"png\";/* global mpl */\n", + "\n", + "var comm_websocket_adapter = function (comm) {\n", + " // Create a \"websocket\"-like object which calls the given IPython comm\n", + " // object with the appropriate methods. Currently this is a non binary\n", + " // socket, so there is still some room for performance tuning.\n", + " var ws = {};\n", + "\n", + " ws.binaryType = comm.kernel.ws.binaryType;\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " function updateReadyState(_event) {\n", + " if (comm.kernel.ws) {\n", + " ws.readyState = comm.kernel.ws.readyState;\n", + " } else {\n", + " ws.readyState = 3; // Closed state.\n", + " }\n", + " }\n", + " comm.kernel.ws.addEventListener('open', updateReadyState);\n", + " comm.kernel.ws.addEventListener('close', updateReadyState);\n", + " comm.kernel.ws.addEventListener('error', updateReadyState);\n", + "\n", + " ws.close = function () {\n", + " comm.close();\n", + " };\n", + " ws.send = function (m) {\n", + " //console.log('sending', m);\n", + " comm.send(m);\n", + " };\n", + " // Register the callback with on_msg.\n", + " comm.on_msg(function (msg) {\n", + " //console.log('receiving', msg['content']['data'], msg);\n", + " var data = msg['content']['data'];\n", + " if (data['blob'] !== undefined) {\n", + " data = {\n", + " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", + " };\n", + " }\n", + " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", + " ws.onmessage(data);\n", + " });\n", + " return ws;\n", + "};\n", + "\n", + "mpl.mpl_figure_comm = function (comm, msg) {\n", + " // This is the function which gets called when the mpl process\n", + " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", + "\n", + " var id = msg.content.data.id;\n", + " // Get hold of the div created by the display call when the Comm\n", + " // socket was opened in Python.\n", + " var element = document.getElementById(id);\n", + " var ws_proxy = comm_websocket_adapter(comm);\n", + "\n", + " function ondownload(figure, _format) {\n", + " window.open(figure.canvas.toDataURL());\n", + " }\n", + "\n", + " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", + "\n", + " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", + " // web socket which is closed, not our websocket->open comm proxy.\n", + " ws_proxy.onopen();\n", + "\n", + " fig.parent_element = element;\n", + " fig.cell_info = mpl.find_output_cell(\"
\");\n", + " if (!fig.cell_info) {\n", + " console.error('Failed to find cell for figure', id, fig);\n", + " return;\n", + " }\n", + " fig.cell_info[0].output_area.element.on(\n", + " 'cleared',\n", + " { fig: fig },\n", + " fig._remove_fig_handler\n", + " );\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_close = function (fig, msg) {\n", + " var width = fig.canvas.width / fig.ratio;\n", + " fig.cell_info[0].output_area.element.off(\n", + " 'cleared',\n", + " fig._remove_fig_handler\n", + " );\n", + " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", + "\n", + " // Update the output cell to use the data from the current canvas.\n", + " fig.push_to_output();\n", + " var dataURL = fig.canvas.toDataURL();\n", + " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", + " // the notebook keyboard shortcuts fail.\n", + " IPython.keyboard_manager.enable();\n", + " fig.parent_element.innerHTML =\n", + " '';\n", + " fig.close_ws(fig, msg);\n", + "};\n", + "\n", + "mpl.figure.prototype.close_ws = function (fig, msg) {\n", + " fig.send_message('closing', msg);\n", + " // fig.ws.close()\n", + "};\n", + "\n", + "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", + " // Turn the data on the canvas into data in the output cell.\n", + " var width = this.canvas.width / this.ratio;\n", + " var dataURL = this.canvas.toDataURL();\n", + " this.cell_info[1]['text/html'] =\n", + " '';\n", + "};\n", + "\n", + "mpl.figure.prototype.updated_canvas_event = function () {\n", + " // Tell IPython that the notebook contents must change.\n", + " IPython.notebook.set_dirty(true);\n", + " this.send_message('ack', {});\n", + " var fig = this;\n", + " // Wait a second, then push the new image to the DOM so\n", + " // that it is saved nicely (might be nice to debounce this).\n", + " setTimeout(function () {\n", + " fig.push_to_output();\n", + " }, 1000);\n", + "};\n", + "\n", + "mpl.figure.prototype._init_toolbar = function () {\n", + " var fig = this;\n", + "\n", + " var toolbar = document.createElement('div');\n", + " toolbar.classList = 'btn-toolbar';\n", + " this.root.appendChild(toolbar);\n", + "\n", + " function on_click_closure(name) {\n", + " return function (_event) {\n", + " return fig.toolbar_button_onclick(name);\n", + " };\n", + " }\n", + "\n", + " function on_mouseover_closure(tooltip) {\n", + " return function (event) {\n", + " if (!event.currentTarget.disabled) {\n", + " return fig.toolbar_button_onmouseover(tooltip);\n", + " }\n", + " };\n", + " }\n", + "\n", + " fig.buttons = {};\n", + " var buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " var button;\n", + " for (var toolbar_ind in mpl.toolbar_items) {\n", + " var name = mpl.toolbar_items[toolbar_ind][0];\n", + " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", + " var image = mpl.toolbar_items[toolbar_ind][2];\n", + " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", + "\n", + " if (!name) {\n", + " /* Instead of a spacer, we start a new button group. */\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + " buttonGroup = document.createElement('div');\n", + " buttonGroup.classList = 'btn-group';\n", + " continue;\n", + " }\n", + "\n", + " button = fig.buttons[name] = document.createElement('button');\n", + " button.classList = 'btn btn-default';\n", + " button.href = '#';\n", + " button.title = name;\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', on_click_closure(method_name));\n", + " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", + " buttonGroup.appendChild(button);\n", + " }\n", + "\n", + " if (buttonGroup.hasChildNodes()) {\n", + " toolbar.appendChild(buttonGroup);\n", + " }\n", + "\n", + " // Add the status bar.\n", + " var status_bar = document.createElement('span');\n", + " status_bar.classList = 'mpl-message pull-right';\n", + " toolbar.appendChild(status_bar);\n", + " this.message = status_bar;\n", + "\n", + " // Add the close button to the window.\n", + " var buttongrp = document.createElement('div');\n", + " buttongrp.classList = 'btn-group inline pull-right';\n", + " button = document.createElement('button');\n", + " button.classList = 'btn btn-mini btn-primary';\n", + " button.href = '#';\n", + " button.title = 'Stop Interaction';\n", + " button.innerHTML = '';\n", + " button.addEventListener('click', function (_evt) {\n", + " fig.handle_close(fig, {});\n", + " });\n", + " button.addEventListener(\n", + " 'mouseover',\n", + " on_mouseover_closure('Stop Interaction')\n", + " );\n", + " buttongrp.appendChild(button);\n", + " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", + " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", + "};\n", + "\n", + "mpl.figure.prototype._remove_fig_handler = function (event) {\n", + " var fig = event.data.fig;\n", + " if (event.target !== this) {\n", + " // Ignore bubbled events from children.\n", + " return;\n", + " }\n", + " fig.close_ws(fig, {});\n", + "};\n", + "\n", + "mpl.figure.prototype._root_extra_style = function (el) {\n", + " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", + "};\n", + "\n", + "mpl.figure.prototype._canvas_extra_style = function (el) {\n", + " // this is important to make the div 'focusable\n", + " el.setAttribute('tabindex', 0);\n", + " // reach out to IPython and tell the keyboard manager to turn it's self\n", + " // off when our div gets focus\n", + "\n", + " // location in version 3\n", + " if (IPython.notebook.keyboard_manager) {\n", + " IPython.notebook.keyboard_manager.register_events(el);\n", + " } else {\n", + " // location in version 2\n", + " IPython.keyboard_manager.register_events(el);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", + " // Check for shift+enter\n", + " if (event.shiftKey && event.which === 13) {\n", + " this.canvas_div.blur();\n", + " // select the cell after this one\n", + " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", + " IPython.notebook.select(index + 1);\n", + " }\n", + "};\n", + "\n", + "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", + " fig.ondownload(fig, null);\n", + "};\n", + "\n", + "mpl.find_output_cell = function (html_output) {\n", + " // Return the cell and output element which can be found *uniquely* in the notebook.\n", + " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", + " // IPython event is triggered only after the cells have been serialised, which for\n", + " // our purposes (turning an active figure into a static one), is too late.\n", + " var cells = IPython.notebook.get_cells();\n", + " var ncells = cells.length;\n", + " for (var i = 0; i < ncells; i++) {\n", + " var cell = cells[i];\n", + " if (cell.cell_type === 'code') {\n", + " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", + " var data = cell.output_area.outputs[j];\n", + " if (data.data) {\n", + " // IPython >= 3 moved mimebundle to data attribute of output\n", + " data = data.data;\n", + " }\n", + " if (data['text/html'] === html_output) {\n", + " return [cell, data, j];\n", + " }\n", + " }\n", + " }\n", + " }\n", + "};\n", + "\n", + "// Register the function which deals with the matplotlib target/channel.\n", + "// The kernel may be null if the page has been refreshed.\n", + "if (IPython.notebook.kernel !== null) {\n", + " IPython.notebook.kernel.comm_manager.register_target(\n", + " 'matplotlib',\n", + " mpl.mpl_figure_comm\n", + " );\n", + "}\n" + ], "text/plain": [ "" ] @@ -990,7 +2933,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -1001,7 +2944,16 @@ } ], "source": [ - "visualization_steps = int(9e4) # currently this crashes for larger values\n", + "motor_dashboard.initialize()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "visualization_steps = int(9e4)\n", "obs = env.reset()\n", "for i in range(visualization_steps):\n", " action, _states = model.predict(obs, deterministic=True)\n", @@ -1027,15 +2979,15 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "The reward per step with 100000 steps was: -1.1316 \n", - "The average Episode length was: 5602 \n" + "The reward per step with 100000 steps was: -1.2836 \n", + "The average Episode length was: 5819 \n" ] } ], From 522edf3c8ebbf042da1785e215a1a2b52755a1d6 Mon Sep 17 00:00:00 2001 From: Arne Date: Wed, 26 Jan 2022 21:43:06 +0100 Subject: [PATCH 29/79] bugfix --- .../pmsm_mpc_dq_current_control.ipynb | 771 +----------------- 1 file changed, 2 insertions(+), 769 deletions(-) diff --git a/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb b/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb index 87285b98..5fbf778c 100644 --- a/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb +++ b/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb @@ -313,774 +313,7 @@ "outputs": [ { "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "window.mpl = {};\n", - "\n", - "\n", - "mpl.get_websocket_type = function() {\n", - " if (typeof(WebSocket) !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof(MozWebSocket) !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert('Your browser does not have WebSocket support. ' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.');\n", - " };\n", - "}\n", - "\n", - "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = (this.ws.binaryType != undefined);\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById(\"mpl-warnings\");\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent = (\n", - " \"This browser does not support binary websocket messages. \" +\n", - " \"Performance may be slow.\");\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = $('
');\n", - " this._root_extra_style(this.root)\n", - " this.root.attr('style', 'display: inline-block');\n", - "\n", - " $(parent_element).append(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", - " fig.send_message(\"send_image_mode\", {});\n", - " if (mpl.ratio != 1) {\n", - " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", - " }\n", - " fig.send_message(\"refresh\", {});\n", - " }\n", - "\n", - " this.imageObj.onload = function() {\n", - " if (fig.image_mode == 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function() {\n", - " fig.ws.close();\n", - " }\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "}\n", - "\n", - "mpl.figure.prototype._init_header = function() {\n", - " var titlebar = $(\n", - " '
');\n", - " var titletext = $(\n", - " '
');\n", - " titlebar.append(titletext)\n", - " this.root.append(titlebar);\n", - " this.header = titletext[0];\n", - "}\n", - "\n", - "\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "\n", - "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", - "\n", - "}\n", - "\n", - "mpl.figure.prototype._init_canvas = function() {\n", - " var fig = this;\n", - "\n", - " var canvas_div = $('
');\n", - "\n", - " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", - "\n", - " function canvas_keyboard_event(event) {\n", - " return fig.key_event(event, event['data']);\n", - " }\n", - "\n", - " canvas_div.keydown('key_press', canvas_keyboard_event);\n", - " canvas_div.keyup('key_release', canvas_keyboard_event);\n", - " this.canvas_div = canvas_div\n", - " this._canvas_extra_style(canvas_div)\n", - " this.root.append(canvas_div);\n", - "\n", - " var canvas = $('');\n", - " canvas.addClass('mpl-canvas');\n", - " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", - "\n", - " this.canvas = canvas[0];\n", - " this.context = canvas[0].getContext(\"2d\");\n", - "\n", - " var backingStore = this.context.backingStorePixelRatio ||\n", - "\tthis.context.webkitBackingStorePixelRatio ||\n", - "\tthis.context.mozBackingStorePixelRatio ||\n", - "\tthis.context.msBackingStorePixelRatio ||\n", - "\tthis.context.oBackingStorePixelRatio ||\n", - "\tthis.context.backingStorePixelRatio || 1;\n", - "\n", - " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband = $('');\n", - " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", - "\n", - " var pass_mouse_events = true;\n", - "\n", - " canvas_div.resizable({\n", - " start: function(event, ui) {\n", - " pass_mouse_events = false;\n", - " },\n", - " resize: function(event, ui) {\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " stop: function(event, ui) {\n", - " pass_mouse_events = true;\n", - " fig.request_resize(ui.size.width, ui.size.height);\n", - " },\n", - " });\n", - "\n", - " function mouse_event_fn(event) {\n", - " if (pass_mouse_events)\n", - " return fig.mouse_event(event, event['data']);\n", - " }\n", - "\n", - " rubberband.mousedown('button_press', mouse_event_fn);\n", - " rubberband.mouseup('button_release', mouse_event_fn);\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband.mousemove('motion_notify', mouse_event_fn);\n", - "\n", - " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", - " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", - "\n", - " canvas_div.on(\"wheel\", function (event) {\n", - " event = event.originalEvent;\n", - " event['data'] = 'scroll'\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " mouse_event_fn(event);\n", - " });\n", - "\n", - " canvas_div.append(canvas);\n", - " canvas_div.append(rubberband);\n", - "\n", - " this.rubberband = rubberband;\n", - " this.rubberband_canvas = rubberband[0];\n", - " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", - " this.rubberband_context.strokeStyle = \"#000000\";\n", - "\n", - " this._resize_canvas = function(width, height) {\n", - " // Keep the size of the canvas, canvas container, and rubber band\n", - " // canvas in synch.\n", - " canvas_div.css('width', width)\n", - " canvas_div.css('height', height)\n", - "\n", - " canvas.attr('width', width * mpl.ratio);\n", - " canvas.attr('height', height * mpl.ratio);\n", - " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", - "\n", - " rubberband.attr('width', width);\n", - " rubberband.attr('height', height);\n", - " }\n", - "\n", - " // Set the figure to an initial 600x600px, this will subsequently be updated\n", - " // upon first draw.\n", - " this._resize_canvas(600, 600);\n", - "\n", - " // Disable right mouse context menu.\n", - " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", - " return false;\n", - " });\n", - "\n", - " function set_focus () {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "}\n", - "\n", - "mpl.figure.prototype._init_toolbar = function() {\n", - " var fig = this;\n", - "\n", - " var nav_element = $('
');\n", - " nav_element.attr('style', 'width: 100%');\n", - " this.root.append(nav_element);\n", - "\n", - " // Define a callback function for later on.\n", - " function toolbar_event(event) {\n", - " return fig.toolbar_button_onclick(event['data']);\n", - " }\n", - " function toolbar_mouse_event(event) {\n", - " return fig.toolbar_button_onmouseover(event['data']);\n", - " }\n", - "\n", - " for(var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " // put a spacer in here.\n", - " continue;\n", - " }\n", - " var button = $('');\n button.click(method_name, toolbar_event);\n button.mouseover(tooltip, toolbar_mouse_event);\n nav_element.append(button);\n }\n\n // Add the status bar.\n var status_bar = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var button = $('');\n button.click(function (evt) { fig.handle_close(fig, {}); } );\n button.mouseover('Stop Interaction', toolbar_mouse_event);\n buttongrp.append(button);\n var titlebar = this.root.find($('.ui-dialog-titlebar'));\n titlebar.prepend(buttongrp);\n}\n\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.figure.prototype._canvas_extra_style = function(el){\n // this is important to make the div 'focusable\n el.attr('tabindex', 0)\n // reach out to IPython and tell the keyboard manager to turn it's self\n // off when our div gets focus\n\n // location in version 3\n if (IPython.notebook.keyboard_manager) {\n IPython.notebook.keyboard_manager.register_events(el);\n }\n else {\n // location in version 2\n IPython.keyboard_manager.register_events(el);\n }\n\n}\n\nmpl.figure.prototype._key_event_extra = function(event, name) {\n var manager = IPython.notebook.keyboard_manager;\n if (!manager)\n manager = IPython.keyboard_manager;\n\n // Check for shift+enter\n if (event.shiftKey && event.which == 13) {\n this.canvas_div.blur();\n // select the cell after this one\n var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n IPython.notebook.select(index + 1);\n }\n}\n\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.find_output_cell = function(html_output) {\n // Return the cell and output element which can be found *uniquely* in the notebook.\n // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n // IPython event is triggered only after the cells have been serialised, which for\n // our purposes (turning an active figure into a static one), is too late.\n var cells = IPython.notebook.get_cells();\n var ncells = cells.length;\n for (var i=0; i= 3 moved mimebundle to data attribute of output\n data = data.data;\n }\n if (data['text/html'] == html_output) {\n return [cell, data, j];\n }\n }\n }\n }\n}\n\n// Register the function which deals with the matplotlib target/channel.\n// The kernel may be null if the page has been refreshed.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n", "text/plain": [ "" ] @@ -1124,7 +357,7 @@ " ),\n", " \n", " # definitions for the setting parameters\n", - " supply=dict(u_sup=400),\n", + " supply=dict(u_nominal=400),\n", " motor=dict(\n", " motor_parameter=motor_parameter,\n", " limit_values=limit_values,\n", From 6abcfb8f0634a66e72766dccdc403ce1f1984e4b Mon Sep 17 00:00:00 2001 From: Arne Date: Thu, 27 Jan 2022 16:22:32 +0100 Subject: [PATCH 30/79] gitignore update --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 8c9ea460..f6c3b0be 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ examples/logs/ .coverage /my_examples/ +.vscode/settings.json +.vscode/launch.json From 134da43a5ff0e86f1bf6ab0e5fbfe766bfdbee89 Mon Sep 17 00:00:00 2001 From: Arne Date: Sat, 5 Feb 2022 21:51:53 +0100 Subject: [PATCH 31/79] design change --- .../dq_to_abc_action_processor.py | 98 ++++++++++--------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py index ab93cf53..45f24888 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -23,7 +23,20 @@ def _transformation(action, angle): """ return ps.ThreePhaseMotor.t_32(ps.ThreePhaseMotor.q_inv(action, angle)) - def __init__(self, angle_name=None, physical_system=None): + _registry = {} + + @classmethod + def register_transformation(cls, motor_types): + def wrapper(callable_): + for motor_type in motor_types: + cls._registry[motor_type] = callable_ + return wrapper + + def __new__(cls, motor_type, *args, **kwargs): + assert motor_type in cls._registry.keys(), f'Not supported motor_type {motor_type}.' + return cls._registry[motor_type](*args, **kwargs) + + def __init__(self, angle_name, physical_system=None): """ Args: angle_name(string): Name of the state that defines the current angle of the AC-motor @@ -44,25 +57,10 @@ def set_physical_system(self, physical_system): 'The motor in the system has to derive from the ThreePhaseMotor to define transformations.' super().set_physical_system(physical_system) self._omega_index = physical_system.state_names.index('omega') - if isinstance(physical_system.unwrapped, ps.DoublyFedInductionMotorSystem): - self._set_dfim_system(physical_system) - else: - self._action_space = gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) - # If no angle name was passed, try to use defaults. - # ('epsilon' for sync motors 'psi_angle' for induction motors) - if self._angle_name is None: - if isinstance( - physical_system.electrical_motor, - (ps.PermanentMagnetSynchronousMotor, ps.SynchronousReluctanceMotor) - ): - self._angle_name = 'epsilon' - elif isinstance(physical_system.electrical_motor, ps.SquirrelCageInductionMotor): - self._angle_name = 'psi_angle' - self.simulate = self._single_angle_simulate - self._angle_index = physical_system.state_names.index(self._angle_name) - assert self._angle_name in physical_system.state_names, \ - f'Angle {self._angle_name} not in the states of the physical system. ' \ - f'Probably a flux observer is required.' + self._angle_index = physical_system.state_names.index(self._angle_name) + assert self._angle_name in physical_system.state_names, \ + f'Angle {self._angle_name} not in the states of the physical system. ' \ + f'Probably a flux observer is required.' self._angle_advance = 0.5 @@ -72,33 +70,51 @@ def set_physical_system(self, physical_system): return self - def _set_dfim_system(self, physical_system): - self._action_space = gym.spaces.Box(-1, 1, shape=(4,), dtype=np.float64) - self._angle_index = [ - physical_system.state_names.index('psi_angle'), physical_system.state_names.index('epsilon') - ] - self.simulate = self._dfim_simulate - def simulate(self, action): + raise NotImplementedError + + def reset(self, **kwargs): # Docstring of super class - raise ReferenceError('Physical System is unset.') + normalized_state = self._physical_system.reset() + self._state = normalized_state * self._physical_system.limits + return normalized_state - def _single_angle_simulate(self, action): - """Dq to abc space transformation function for all environments except those for a doubly fed induction motor. + def _advance_angle(self, state): + return state[self._angle_index] \ + + self._angle_advance * self._physical_system.tau * state[self._omega_index] - Args: - action: The actions for the stator and rotor circuit in dq-coordinates. - Returns: - The next state of the physical system. - """ - advanced_angle = self._state[self._angle_index] \ - + self._angle_advance * self._physical_system.tau * self._state[self._omega_index] + +class _ClassicDqToAbcActionProcessor(DqToAbcActionProcessor): + + @property + def action_space(self): + return gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) + + def simulate(self, action): + # Docstring of superclass + advanced_angle = self._advance_angle(self._state) abc_action = self._transformation(action, advanced_angle) normalized_state = self._physical_system.simulate(abc_action) self._state = normalized_state * self._physical_system.limits return normalized_state - def _dfim_simulate(self, action): + +DqToAbcActionProcessor.register_transformation(['PMSM'])( + lambda angle_name='epsilon', *args, **kwargs: _ClassicDqToAbcActionProcessor(angle_name, *args, **kwargs) +) + +DqToAbcActionProcessor.register_transformation(['SCIM'])( + lambda angle_name='psi_angle', *args, **kwargs: _ClassicDqToAbcActionProcessor(angle_name, *args, **kwargs) +) + + +class _DFIMDqToAbcActionProcessor(DqToAbcActionProcessor): + + @property + def action_space(self): + return gym.spaces.Box(-1, 1, shape=(4,)) + + def simulate(self, action): """Dq to abc space transformation function for doubly fed induction motor environments. Args: @@ -116,9 +132,3 @@ def _dfim_simulate(self, action): normalized_state = self._physical_system.simulate(abc_action) self._state = normalized_state * self._physical_system.limits return normalized_state - - def reset(self, **kwargs): - # Docstring of super class - normalized_state = self._physical_system.reset() - self._state = normalized_state * self._physical_system.limits - return normalized_state From 461e8e58a0628172a7c70f9525d9e91a20650514 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 7 Feb 2022 19:05:42 +0100 Subject: [PATCH 32/79] first version: EESM system --- .../physical_systems/physical_systems.py | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/gym_electric_motor/physical_systems/physical_systems.py b/gym_electric_motor/physical_systems/physical_systems.py index 9eed4df0..fc40c438 100644 --- a/gym_electric_motor/physical_systems/physical_systems.py +++ b/gym_electric_motor/physical_systems/physical_systems.py @@ -557,6 +557,145 @@ def reset(self, *_): )) return (system_state + noise) / self._limits +class ExternallyExcitedSynchronousMotorSystem(SynchronousMotorSystem): + """SCML-System that can be used with the externally excited synchronous motor (EESM)""" + + def __init__(self, control_space='abc', **kwargs): + """ + Args: + control_space(str):('abc' or 'dq') Choose, if actions the actions space is in dq or abc space + kwargs: Further arguments to pass tp SCMLSystem + """ + super().__init__(**kwargs) + self._action_space = Box(-1,1, shape=(4,), dtype=np.float64) + self.control_space = control_space + if control_space == 'dq': + assert type(self._converter.action_space) == Box, \ + 'dq-control space is only available for Continuous Controlled Converters' + self._action_space = Box(-1, 1, shape=(3,), dtype=np.float64) + + def _build_state_space(self, state_names): + # Docstring of superclass + low = -1 * np.ones_like(state_names, dtype=float) + low[self.U_SUP_IDX] = 0.0 + high = np.ones_like(state_names, dtype=float) + return Box(low, high, dtype=np.float64) + + def _build_state_names(self): + # Docstring of superclass + return self._mechanical_load.state_names \ + + [ + 'torque', 'i_a', 'i_b', 'i_c', 'i_sd', 'i_sq', 'i_e', + 'u_a', 'u_b', 'u_c', 'u_sd', 'u_sq', 'u_e', + 'epsilon', 'u_sup' + ] + + + def _set_indices(self): + # Docstring of superclass + self._omega_ode_idx = self._mechanical_load.OMEGA_IDX + self._load_ode_idx = list(range(len(self._mechanical_load.state_names))) + self._ode_currents_idx = list(range( + self._load_ode_idx[-1] + 1, self._load_ode_idx[-1] + 1 + len(self._electrical_motor.CURRENTS) + )) + self._motor_ode_idx = self._ode_currents_idx + self._motor_ode_idx += [self._motor_ode_idx[-1] + 1] + self._ode_currents_idx = self._motor_ode_idx[:-1] + self.OMEGA_IDX = self.mechanical_load.OMEGA_IDX + self.TORQUE_IDX = len(self.mechanical_load.state_names) + currents_lower = self.TORQUE_IDX + 1 + currents_upper = currents_lower + 5 + self.CURRENTS_IDX = list(range(currents_lower, currents_upper)) + voltages_lower = currents_upper + voltages_upper = voltages_lower + 5 + self.VOLTAGES_IDX = list(range(voltages_lower, voltages_upper)) + self.EPSILON_IDX = voltages_upper + self.U_SUP_IDX = list(range(self.EPSILON_IDX + 1, self.EPSILON_IDX + 1 + self._supply.voltage_len)) + self._ode_epsilon_idx = self._motor_ode_idx[-1] + + def simulate(self, action, *_, **__): + # Docstring of superclass + ode_state = self._ode_solver.y + eps = ode_state[self._ode_epsilon_idx] + if self.control_space == 'dq': + action = self.dq_to_abc_space(action, eps) + i_in = self.dq_to_abc_space(self._electrical_motor.i_in(ode_state[self._ode_currents_idx]), eps) + switching_times = self._converter.set_action(action, self._t) + + for t in switching_times[:-1]: + i_sup = self._converter.i_sup(i_in) + u_sup = self._supply.get_voltage(self._t, i_sup) + u_in = self._converter.convert(i_in, self._ode_solver.t) + u_in = [u * u_s for u in u_in for u_s in u_sup] + u_dq = self.abc_to_dq_space(u_in, eps) + self._ode_solver.set_f_params(u_dq) + ode_state = self._ode_solver.integrate(t) + eps = ode_state[self._ode_epsilon_idx] + i_in = self.dq_to_abc_space(self._electrical_motor.i_in(ode_state[self._ode_currents_idx]), eps) + + i_sup = self._converter.i_sup(i_in) + u_sup = self._supply.get_voltage(self._t, i_sup) + u_in = self._converter.convert(i_in, self._ode_solver.t) + u_in = [u * u_s for u in u_in for u_s in u_sup] + u_dq = self.abc_to_dq_space(u_in, eps) + self._ode_solver.set_f_params(u_dq) + ode_state = self._ode_solver.integrate(self._t + self._tau) + self._t = self._ode_solver.t + self._k += 1 + torque = self._electrical_motor.torque(ode_state[self._motor_ode_idx]) + noise = self._noise_generator.noise() + mechanical_state = ode_state[self._load_ode_idx] + i_dq = ode_state[self._ode_currents_idx] + i_abc = list( + self.dq_to_abc_space(i_dq, eps) + ) + eps = ode_state[self._ode_epsilon_idx] % (2 * np.pi) + if eps > np.pi: + eps -= 2 * np.pi + + system_state = np.concatenate(( + mechanical_state, + [torque], + i_abc, i_dq, + u_in, u_dq, + [eps], + u_sup + )) + return (system_state + noise) / self._limits + + def reset(self, *_): + # Docstring of superclass + motor_state = self._electrical_motor.reset( + state_space=self.state_space, + state_positions=self.state_positions) + mechanical_state = self._mechanical_load.reset( + state_positions=self.state_positions, + state_space=self.state_space, + nominal_state=self.nominal_state) + ode_state = np.concatenate((mechanical_state, motor_state)) + u_sup = self.supply.reset() + eps = ode_state[self._ode_epsilon_idx] + if eps > np.pi: + eps -= 2 * np.pi + u_abc = self.converter.reset() + u_abc = [u * u_s for u in u_abc for u_s in u_sup] + u_dq = self.abc_to_dq_space(u_abc, eps) + i_dq = ode_state[self._ode_currents_idx] + i_abc = self.dq_to_abc_space(i_dq, eps) + torque = self.electrical_motor.torque(motor_state) + noise = self._noise_generator.reset() + self._t = 0 + self._k = 0 + self._ode_solver.set_initial_value(ode_state, self._t) + system_state = np.concatenate(( + mechanical_state, + [torque], + i_abc, i_dq, + u_abc, u_dq, + [eps], + u_sup, + )) + return (system_state + noise) / self._limits class SquirrelCageInductionMotorSystem(ThreePhaseMotorSystem): """ From 98ccd89afb9364f54d7f29eb228c07c2d628b96c Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 7 Feb 2022 19:29:52 +0100 Subject: [PATCH 33/79] Changed class structure in the DQ-Abc Processors --- .gitignore | 3 ++ .../dq_to_abc_action_processor.py | 32 +++++++++++++------ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 8c9ea460..19c2cc63 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ examples/logs/ .coverage /my_examples/ +gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +.vscode/settings.json +.vscode/launch.json diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py index 45f24888..7cb8585a 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -27,14 +27,18 @@ def _transformation(action, angle): @classmethod def register_transformation(cls, motor_types): - def wrapper(callable_): - for motor_type in motor_types: - cls._registry[motor_type] = callable_ - return wrapper + def wrapper(callable_): + for motor_type in motor_types: + cls._registry[motor_type] = callable_ + return wrapper - def __new__(cls, motor_type, *args, **kwargs): + @classmethod + def make(cls, motor_type, *args, **kwargs): assert motor_type in cls._registry.keys(), f'Not supported motor_type {motor_type}.' - return cls._registry[motor_type](*args, **kwargs) + class_ = cls._registry[motor_type] + inst = class_(*args, **kwargs) + return inst + def __init__(self, angle_name, physical_system=None): """ @@ -108,12 +112,18 @@ def simulate(self, action): ) +@DqToAbcActionProcessor.register_transformation(['DFIM']) class _DFIMDqToAbcActionProcessor(DqToAbcActionProcessor): @property def action_space(self): return gym.spaces.Box(-1, 1, shape=(4,)) + def __init__(self, physical_system=None): + super().__init__('epsilon', physical_system=physical_system) + self._flux_angle_name = 'psi_abs' + self._flux_angle_index = None + def simulate(self, action): """Dq to abc space transformation function for doubly fed induction motor environments. @@ -122,13 +132,17 @@ def simulate(self, action): Returns: The next state of the physical system. """ - advanced_angle = self._state[self._angle_index[1]] \ - + self._angle_advance * self._physical_system.tau * self._state[self._omega_index] + advanced_angle = self._advance_angle(self._state) dq_action_stator = action[:2] dq_action_rotor = action[2:] abc_action_stator = self._transformation(dq_action_stator, advanced_angle) - abc_action_rotor = self._transformation(dq_action_rotor, self._angle_index[0] - advanced_angle) + abc_action_rotor = self._transformation(dq_action_rotor, self._state[self._flux_angle_index] - advanced_angle) abc_action = np.concatenate((abc_action_stator, abc_action_rotor)) normalized_state = self._physical_system.simulate(abc_action) self._state = normalized_state * self._physical_system.limits return normalized_state + + def set_physical_system(self, physical_system): + super().set_physical_system(physical_system) + self._flux_angle_index = physical_system.state_names.index('psi_angle') + return self \ No newline at end of file From 755bf8d2a59e89211b3f974e5941fdae4db8ccae Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 7 Feb 2022 19:40:39 +0100 Subject: [PATCH 34/79] Test fixes --- .../state_action_processors/dq_to_abc_action_processor.py | 1 - .../test_dq_to_abc_action_processor.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py index 7cb8585a..e505b6d8 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -39,7 +39,6 @@ def make(cls, motor_type, *args, **kwargs): inst = class_(*args, **kwargs) return inst - def __init__(self, angle_name, physical_system=None): """ Args: diff --git a/tests/test_state_action_processors/test_dq_to_abc_action_processor.py b/tests/test_state_action_processors/test_dq_to_abc_action_processor.py index e65573aa..7e245ce0 100644 --- a/tests/test_state_action_processors/test_dq_to_abc_action_processor.py +++ b/tests/test_state_action_processors/test_dq_to_abc_action_processor.py @@ -17,7 +17,7 @@ def physical_system(self): @pytest.fixture def processor(self, physical_system): - return gem.state_action_processors.DqToAbcActionProcessor(physical_system=physical_system) + return gem.state_action_processors.DqToAbcActionProcessor.make('PMSM', physical_system=physical_system) def test_action_space(self, processor, physical_system): space = gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) From 6468d2f54d71a3ab7a8e3451270d880554fcead6 Mon Sep 17 00:00:00 2001 From: Arne Date: Wed, 9 Feb 2022 21:37:52 +0100 Subject: [PATCH 35/79] First version of physical system for EESM --- .../physical_systems/physical_systems.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/gym_electric_motor/physical_systems/physical_systems.py b/gym_electric_motor/physical_systems/physical_systems.py index fc40c438..34501205 100644 --- a/gym_electric_motor/physical_systems/physical_systems.py +++ b/gym_electric_motor/physical_systems/physical_systems.py @@ -604,10 +604,10 @@ def _set_indices(self): self.OMEGA_IDX = self.mechanical_load.OMEGA_IDX self.TORQUE_IDX = len(self.mechanical_load.state_names) currents_lower = self.TORQUE_IDX + 1 - currents_upper = currents_lower + 5 + currents_upper = currents_lower + 6 self.CURRENTS_IDX = list(range(currents_lower, currents_upper)) voltages_lower = currents_upper - voltages_upper = voltages_lower + 5 + voltages_upper = voltages_lower + 6 self.VOLTAGES_IDX = list(range(voltages_lower, voltages_upper)) self.EPSILON_IDX = voltages_upper self.U_SUP_IDX = list(range(self.EPSILON_IDX + 1, self.EPSILON_IDX + 1 + self._supply.voltage_len)) @@ -619,25 +619,27 @@ def simulate(self, action, *_, **__): eps = ode_state[self._ode_epsilon_idx] if self.control_space == 'dq': action = self.dq_to_abc_space(action, eps) - i_in = self.dq_to_abc_space(self._electrical_motor.i_in(ode_state[self._ode_currents_idx]), eps) + i_in_dq = self._electrical_motor.i_in(ode_state[self._ode_currents_idx]) + i_in_abc = list(self.dq_to_abc_space(i_in_dq[:2], eps)) + i_in_dq[2:] switching_times = self._converter.set_action(action, self._t) for t in switching_times[:-1]: - i_sup = self._converter.i_sup(i_in) + i_sup = self._converter.i_sup(i_in_abc) u_sup = self._supply.get_voltage(self._t, i_sup) - u_in = self._converter.convert(i_in, self._ode_solver.t) + u_in = self._converter.convert(i_in_abc, self._ode_solver.t) u_in = [u * u_s for u in u_in for u_s in u_sup] - u_dq = self.abc_to_dq_space(u_in, eps) + u_dq = list(self.abc_to_dq_space(u_in[:2], eps)) + u_in[2:] self._ode_solver.set_f_params(u_dq) ode_state = self._ode_solver.integrate(t) eps = ode_state[self._ode_epsilon_idx] - i_in = self.dq_to_abc_space(self._electrical_motor.i_in(ode_state[self._ode_currents_idx]), eps) + i_in_dq = self._electrical_motor.i_in(ode_state[self._ode_currents_idx]) + i_in_abc = list(self.dq_to_abc_space(i_in_dq[:2], eps)) + i_in_dq[2:] - i_sup = self._converter.i_sup(i_in) + i_sup = self._converter.i_sup(i_in_abc) u_sup = self._supply.get_voltage(self._t, i_sup) - u_in = self._converter.convert(i_in, self._ode_solver.t) + u_in = self._converter.convert(i_in_abc, self._ode_solver.t) u_in = [u * u_s for u in u_in for u_s in u_sup] - u_dq = self.abc_to_dq_space(u_in, eps) + u_dq = self.abc_to_dq_space(u_in[:2], eps) + u_in[2:] self._ode_solver.set_f_params(u_dq) ode_state = self._ode_solver.integrate(self._t + self._tau) self._t = self._ode_solver.t @@ -681,7 +683,7 @@ def reset(self, *_): u_abc = [u * u_s for u in u_abc for u_s in u_sup] u_dq = self.abc_to_dq_space(u_abc, eps) i_dq = ode_state[self._ode_currents_idx] - i_abc = self.dq_to_abc_space(i_dq, eps) + i_abc = self.dq_to_abc_space(i_dq[:2], eps) torque = self.electrical_motor.torque(motor_state) noise = self._noise_generator.reset() self._t = 0 From 19bfb78d9d0524c64ad1469702d804c969f6701e Mon Sep 17 00:00:00 2001 From: Arne Date: Thu, 10 Feb 2022 20:27:56 +0100 Subject: [PATCH 36/79] generated eesm envs --- gym_electric_motor/__init__.py | 47 +++++ gym_electric_motor/envs/__init__.py | 10 ++ gym_electric_motor/envs/gym_eesm/__init__.py | 9 + .../envs/gym_eesm/abccont_cc_eesm_env.py | 168 ++++++++++++++++++ .../envs/gym_eesm/abccont_sc_eesm_env.py | 150 ++++++++++++++++ .../envs/gym_eesm/abccont_tc_eesm_env.py | 155 ++++++++++++++++ .../envs/gym_eesm/dqcont_cc_eesm_env.py | 160 +++++++++++++++++ .../envs/gym_eesm/dqcont_sc_eesm_env.py | 150 ++++++++++++++++ .../envs/gym_eesm/dqcont_tc_eesm_env.py | 155 ++++++++++++++++ .../envs/gym_eesm/finite_cc_eesm_env.py | 159 +++++++++++++++++ .../envs/gym_eesm/finite_sc_eesm_env.py | 149 ++++++++++++++++ .../envs/gym_eesm/finite_tc_eesm_env.py | 148 +++++++++++++++ .../physical_systems/__init__.py | 7 +- .../electric_motors/__init__.py | 1 + .../externally_excited_synchronous_motor.py | 6 +- .../physical_systems/physical_systems.py | 4 +- 16 files changed, 1471 insertions(+), 7 deletions(-) create mode 100644 gym_electric_motor/envs/gym_eesm/__init__.py create mode 100644 gym_electric_motor/envs/gym_eesm/abccont_cc_eesm_env.py create mode 100644 gym_electric_motor/envs/gym_eesm/abccont_sc_eesm_env.py create mode 100644 gym_electric_motor/envs/gym_eesm/abccont_tc_eesm_env.py create mode 100644 gym_electric_motor/envs/gym_eesm/dqcont_cc_eesm_env.py create mode 100644 gym_electric_motor/envs/gym_eesm/dqcont_sc_eesm_env.py create mode 100644 gym_electric_motor/envs/gym_eesm/dqcont_tc_eesm_env.py create mode 100644 gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py create mode 100644 gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py create mode 100644 gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py diff --git a/gym_electric_motor/__init__.py b/gym_electric_motor/__init__.py index dc150a1d..86e233d0 100644 --- a/gym_electric_motor/__init__.py +++ b/gym_electric_motor/__init__.py @@ -202,6 +202,53 @@ **registration_kwargs ) +# Externally Excited Synchronous Motor Environments +register( + id='Finite-SC-EESM-v0', + entry_point=envs_path+'FiniteSpeedControlExternallyExcitedSynchronousMotorEnv', + **registration_kwargs +) +register( + id='Finite-TC-EESM-v0', + entry_point=envs_path+'FiniteTorqueControlExternallyExcitedSynchronousMotorEnv', + **registration_kwargs +) +register( + id='Finite-CC-EESM-v0', + entry_point=envs_path+'FiniteCurrentControlExternallyExcitedSynchronousMotorEnv', + **registration_kwargs +) +register( + id='AbcCont-CC-EESM-v0', + entry_point=envs_path+'AbcContCurrentControlExternallyExcitedSynchronousMotorEnv', + **registration_kwargs +) +register( + id='AbcCont-TC-EESM-v0', + entry_point=envs_path+'AbcContTorqueControlExternallyExcitedSynchronousMotorEnv', + **registration_kwargs +) +register( + id='AbcCont-SC-EESM-v0', + entry_point=envs_path+'AbcContSpeedControlExternallyExcitedSynchronousMotorEnv', + **registration_kwargs +) +register( + id='DqCont-CC-EESM-v0', + entry_point=envs_path+'DqContCurrentControlExternallyExcitedSynchronousMotorEnv', + **registration_kwargs +) +register( + id='DqCont-TC-EESM-v0', + entry_point=envs_path+'DqContTorqueControlExternallyExcitedSynchronousMotorEnv', + **registration_kwargs +) +register( + id='DqCont-SC-EESM-v0', + entry_point=envs_path+'DqContSpeedControlExternallyExcitedSynchronousMotorEnv', + **registration_kwargs +) + # Synchronous Reluctance Motor Environments register( id='Finite-SC-SynRM-v0', diff --git a/gym_electric_motor/envs/__init__.py b/gym_electric_motor/envs/__init__.py index d0d566fb..c83a24f5 100644 --- a/gym_electric_motor/envs/__init__.py +++ b/gym_electric_motor/envs/__init__.py @@ -37,6 +37,16 @@ from .gym_pmsm.dqcont_tc_pmsm_env import DqContTorqueControlPermanentMagnetSynchronousMotorEnv from .gym_pmsm.dqcont_cc_pmsm_env import DqContCurrentControlPermanentMagnetSynchronousMotorEnv +from .gym_eesm.finite_sc_eesm_env import FiniteSpeedControlExternallyExcitedSynchronousMotorEnv +from .gym_eesm.finite_cc_eesm_env import FiniteCurrentControlExternallyExcitedSynchronousMotorEnv +from .gym_eesm.finite_tc_eesm_env import FiniteTorqueControlExternallyExcitedSynchronousMotorEnv +from .gym_eesm.abccont_cc_eesm_env import AbcContCurrentControlExternallyExcitedSynchronousMotorEnv +from .gym_eesm.abccont_sc_eesm_env import AbcContSpeedControlExternallyExcitedSynchronousMotorEnv +from .gym_eesm.abccont_tc_eesm_env import AbcContTorqueControlExternallyExcitedSynchronousMotorEnv +from .gym_eesm.dqcont_sc_eesm_env import DqContSpeedControlExternallyExcitedSynchronousMotorEnv +from .gym_eesm.dqcont_tc_eesm_env import DqContTorqueControlExternallyExcitedSynchronousMotorEnv +from .gym_eesm.dqcont_cc_eesm_env import DqContCurrentControlExternallyExcitedSynchronousMotorEnv + from .gym_synrm.finite_sc_synrm_env import FiniteSpeedControlSynchronousReluctanceMotorEnv from .gym_synrm.finite_cc_synrm_env import FiniteCurrentControlSynchronousReluctanceMotorEnv from .gym_synrm.finite_tc_synrm_env import FiniteTorqueControlSynchronousReluctanceMotorEnv diff --git a/gym_electric_motor/envs/gym_eesm/__init__.py b/gym_electric_motor/envs/gym_eesm/__init__.py new file mode 100644 index 00000000..0a51000a --- /dev/null +++ b/gym_electric_motor/envs/gym_eesm/__init__.py @@ -0,0 +1,9 @@ +from .dqcont_cc_eesm_env import DqContCurrentControlExternallyExcitedSynchronousMotorEnv +from .dqcont_tc_eesm_env import DqContTorqueControlExternallyExcitedSynchronousMotorEnv +from .dqcont_sc_eesm_env import DqContSpeedControlExternallyExcitedSynchronousMotorEnv +from .abccont_sc_eesm_env import AbcContSpeedControlExternallyExcitedSynchronousMotorEnv +from .abccont_tc_eesm_env import AbcContTorqueControlExternallyExcitedSynchronousMotorEnv +from .abccont_cc_eesm_env import AbcContCurrentControlExternallyExcitedSynchronousMotorEnv +from .finite_tc_eesm_env import FiniteTorqueControlExternallyExcitedSynchronousMotorEnv +from .finite_sc_eesm_env import FiniteSpeedControlExternallyExcitedSynchronousMotorEnv +from .finite_cc_eesm_env import FiniteCurrentControlExternallyExcitedSynchronousMotorEnv diff --git a/gym_electric_motor/envs/gym_eesm/abccont_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/abccont_cc_eesm_env.py new file mode 100644 index 00000000..7f4d2137 --- /dev/null +++ b/gym_electric_motor/envs/gym_eesm/abccont_cc_eesm_env.py @@ -0,0 +1,168 @@ +from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ + ElectricMotorVisualization +from gym_electric_motor.physical_systems.physical_systems import ExternallyExcitedSynchronousMotorSystem +from gym_electric_motor.visualization import MotorDashboard +from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.constraints import SquaredConstraint + + +class AbcContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate a abc-domain continuous control set current controlled permanent magnet synchr. motor. + + Key: + ``'AbcCont-CC-EESM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.ContB6BridgeConverter` + - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Load: :py:class:`.ConstantSpeedLoad` + - Ode-Solver: :py:class:`.EulerSolver` + - Noise: **None** + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'i_sd' = 0.5, 'i_sq' = 0.5`` + + - Visualization: :py:class:`.MotorDashboard` current and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + + State Variables: + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` + + Reference Variables: + ``['i_sd', 'i_sq']`` + + Control Cycle Time: + tau = 1e-4 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=13 * [-1], high=13 * [1]) + + Reference Space: + Box(low=[-1, -1], high=[1, 1]) + + Action Space: + Box(low=[-1, -1, -1], high=[1, 1, 1]) + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> + >>> # Select a different ode_solver with default parameters by passing a keystring + >>> my_overridden_solver = 'scipy-solve_ivp' + >>> + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='i_sq', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... 'AbcCont-CC-EESM-v0', + ... voltage_supply=my_changed_voltage_supply_args, + ... ode_solver=my_overridden_solver, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> done = True + >>> for _ in range(1000): + >>> if done: + >>> state, reference = env.reset() + >>> env.render() + >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) + """ + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + default_subgenerators = ( + WienerProcessReferenceGenerator(reference_state='i_sd'), + WienerProcessReferenceGenerator(reference_state='i_sq') + ) + default_subconverters = ( + ps.ContB6BridgeConverter(), + ps.ContFourQuadrantConverter() + ) + physical_system = ExternallyExcitedSynchronousMotorSystem( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=300.0)), + converter=initialize( + ps.PowerElectronicConverter, + converter, + ps.ContMultiConverter, + dict(subconverters=default_subconverters) + ), + motor=initialize(ps.ElectricMotor, motor, ps.ExternallyExcitedSynchronousMotor, dict()), + load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), + calc_jacobian=calc_jacobian, + tau=tau, + control_space='abc' + ) + reference_generator = initialize( + ReferenceGenerator, + reference_generator, + MultipleReferenceGenerator, + dict(sub_generators=default_subgenerators) + ) + reward_function = initialize( + RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) + ) + visualization = initialize( + ElectricMotorVisualization, + visualization, + MotorDashboard, + dict(state_plots=('i_sd', 'i_sq'), action_plots='all') + ) + super().__init__( + physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + ) diff --git a/gym_electric_motor/envs/gym_eesm/abccont_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/abccont_sc_eesm_env.py new file mode 100644 index 00000000..85c8b012 --- /dev/null +++ b/gym_electric_motor/envs/gym_eesm/abccont_sc_eesm_env.py @@ -0,0 +1,150 @@ +from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ + ElectricMotorVisualization +from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem +from gym_electric_motor.visualization import MotorDashboard +from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.constraints import SquaredConstraint + + +class AbcContSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate a abc-domain continuous control set speed controlled permanent magnet synchr. motor. + + Key: + ``'AbcCont-SC-PMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.ContB6BridgeConverter` + - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Load: :py:class:`.PolynomialStaticLoad` + - Ode-Solver: :py:class:`.EulerSolver` + - Noise: **None** + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'omega' = 1.0`` + + - Visualization: :py:class:`.MotorDashboard` current and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + + State Variables: + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` + + Reference Variables: + ``['omega']`` + + Control Cycle Time: + tau = 1e-4 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=13 * [-1], high=13 * [1]) + + Reference Space: + Box(low=[-1, -1], high=[1, 1]) + + Action Space: + Box(low=[-1, -1, -1], high=[1, 1, 1]) + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> + >>> # Select a different ode_solver with default parameters by passing a keystring + >>> my_overridden_solver = 'scipy.solve_ivp' + >>> + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='omega', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... 'AbcCont-SC-PMSM-v0', + ... voltage_supply=my_changed_voltage_supply_args, + ... ode_solver=my_overridden_solver, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> done = True + >>> for _ in range(1000): + >>> if done: + >>> state, reference = env.reset() + >>> env.render() + >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) + """ + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + physical_system = SynchronousMotorSystem( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), + converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), + motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), + load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict( + load_parameter=dict(a=0.01, b=0.01, c=0.0) + )), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), + calc_jacobian=calc_jacobian, + tau=tau, + control_space='abc', + ) + reference_generator = initialize( + ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, dict(reference_state='omega') + ) + reward_function = initialize( + RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(omega=1.0)) + ) + visualization = initialize( + ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) + super().__init__( + physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + ) diff --git a/gym_electric_motor/envs/gym_eesm/abccont_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/abccont_tc_eesm_env.py new file mode 100644 index 00000000..d0c368ca --- /dev/null +++ b/gym_electric_motor/envs/gym_eesm/abccont_tc_eesm_env.py @@ -0,0 +1,155 @@ +from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ + ElectricMotorVisualization +from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem +from gym_electric_motor.visualization import MotorDashboard +from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.constraints import SquaredConstraint + + +class AbcContTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate a abc-domain continuous control set torque controlled permanent magnet synchr. motor. + + Key: + ``'AbcCont-TC-PMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.ContB6BridgeConverter` + - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Load: :py:class:`.ConstantSpeedLoad` + - Ode-Solver: :py:class:`.EulerSolver` + - Noise: **None** + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'torque' = 1.0`` + + - Visualization: :py:class:`.MotorDashboard` torque and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + + State Variables: + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` + + Reference Variables: + ``['torque']`` + + Control Cycle Time: + tau = 1e-4 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=13 * [-1], high=13 * [1]) + + Reference Space: + Box(low=[-1, -1], high=[1, 1]) + + Action Space: + Box(low=[-1, -1, -1], high=[1, 1, 1]) + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> + >>> # Select a different ode_solver with default parameters by passing a keystring + >>> my_overridden_solver = 'scipy.solve_ivp' + >>> + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='torque', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... 'AbcCont-TC-PMSM-v0', + ... voltage_supply=my_changed_voltage_supply_args, + ... ode_solver=my_overridden_solver, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> done = True + >>> for _ in range(1000): + >>> if done: + >>> state, reference = env.reset() + >>> env.render() + >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) + """ + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + physical_system = SynchronousMotorSystem( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), + converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), + motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), + load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), + calc_jacobian=calc_jacobian, + tau=tau, + control_space='abc' + ) + reference_generator = initialize( + ReferenceGenerator, + reference_generator, + WienerProcessReferenceGenerator, + dict(reference_state='torque') + ) + reward_function = initialize( + RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(torque=1.0)) + ) + visualization = initialize( + ElectricMotorVisualization, + visualization, + MotorDashboard, + dict(state_plots=('torque',), action_plots='all') + ) + super().__init__( + physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + ) diff --git a/gym_electric_motor/envs/gym_eesm/dqcont_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/dqcont_cc_eesm_env.py new file mode 100644 index 00000000..379cdf12 --- /dev/null +++ b/gym_electric_motor/envs/gym_eesm/dqcont_cc_eesm_env.py @@ -0,0 +1,160 @@ +from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ + ElectricMotorVisualization +from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem +from gym_electric_motor.visualization import MotorDashboard +from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.constraints import SquaredConstraint + + +class DqContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate a dq-domain continuous control set current controlled permanent magnet synchr. motor. + + Key: + ``'DqCont-CC-PMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.ContB6BridgeConverter` + - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Load: :py:class:`.ConstantSpeedLoad` + - Ode-Solver: :py:class:`.EulerSolver` + - Noise: **None** + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'i_sd' = 0.5, 'i_sq' = 0.5`` + + - Visualization: :py:class:`.MotorDashboard` current and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + + State Variables: + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` + + Reference Variables: + ``['i_sd', 'i_sq']`` + + Control Cycle Time: + tau = 1e-4 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=13 * [-1], high=13 * [1]) + + Reference Space: + Box(low=[-1, -1], high=[1, 1]) + + Action Space: + Box(low=[-1, -1], high=[1, 1]) + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> + >>> # Select a different ode_solver with default parameters by passing a keystring + >>> my_overridden_solver = 'scipy.solve_ivp' + >>> + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='i_sq', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... 'DqCont-CC-PMSM-v0', + ... voltage_supply=my_changed_voltage_supply_args, + ... ode_solver=my_overridden_solver, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> done = True + >>> for _ in range(1000): + >>> if done: + >>> state, reference = env.reset() + >>> env.render() + >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) + """ + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + default_subgenerators = ( + WienerProcessReferenceGenerator(reference_state='i_sd'), + WienerProcessReferenceGenerator(reference_state='i_sq') + ) + + physical_system = SynchronousMotorSystem( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), + converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), + motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), + load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), + calc_jacobian=calc_jacobian, + tau=tau, + control_space='dq' + ) + reference_generator = initialize( + ReferenceGenerator, + reference_generator, + MultipleReferenceGenerator, + dict(sub_generators=default_subgenerators) + ) + reward_function = initialize( + RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) + ) + visualization = initialize( + ElectricMotorVisualization, + visualization, + MotorDashboard, + dict(state_plots=('i_sd', 'i_sq'), action_plots='all') + ) + super().__init__( + physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + ) diff --git a/gym_electric_motor/envs/gym_eesm/dqcont_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/dqcont_sc_eesm_env.py new file mode 100644 index 00000000..5d1bc71c --- /dev/null +++ b/gym_electric_motor/envs/gym_eesm/dqcont_sc_eesm_env.py @@ -0,0 +1,150 @@ +from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ + ElectricMotorVisualization +from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem +from gym_electric_motor.visualization import MotorDashboard +from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.constraints import SquaredConstraint + + +class DqContSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate a dq-domain continuous control set speed controlled permanent magnet synchr. motor. + + Key: + ``'DqCont-SC-PMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.ContB6BridgeConverter` + - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Load: :py:class:`.PolynomialStaticLoad` + - Ode-Solver: :py:class:`.EulerSolver` + - Noise: **None** + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'omega' = 1.0`` + + - Visualization: :py:class:`.MotorDashboard` current and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + + State Variables: + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` + + Reference Variables: + ``['omega']`` + + Control Cycle Time: + tau = 1e-4 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=13 * [-1], high=13 * [1]) + + Reference Space: + Box(low=[-1, -1], high=[1, 1]) + + Action Space: + Box(low=[-1, -1], high=[1, 1]) + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> + >>> # Select a different ode_solver with default parameters by passing a keystring + >>> my_overridden_solver = 'scipy.solve_ivp' + >>> + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='omega', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... 'DqCont-SC-PMSM-v0', + ... voltage_supply=my_changed_voltage_supply_args, + ... ode_solver=my_overridden_solver, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> done = True + >>> for _ in range(1000): + >>> if done: + >>> state, reference = env.reset() + >>> env.render() + >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) + """ + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + physical_system = SynchronousMotorSystem( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), + converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), + motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), + load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict( + load_parameter=dict(a=0.01, b=0.01, c=0.0) + )), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), + calc_jacobian=calc_jacobian, + tau=tau, + control_space='dq', + ) + reference_generator = initialize( + ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, dict(reference_state='omega') + ) + reward_function = initialize( + RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(omega=1.0)) + ) + visualization = initialize( + ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) + super().__init__( + physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + ) diff --git a/gym_electric_motor/envs/gym_eesm/dqcont_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/dqcont_tc_eesm_env.py new file mode 100644 index 00000000..7bc01a7d --- /dev/null +++ b/gym_electric_motor/envs/gym_eesm/dqcont_tc_eesm_env.py @@ -0,0 +1,155 @@ +from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ + ElectricMotorVisualization +from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem +from gym_electric_motor.visualization import MotorDashboard +from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.constraints import SquaredConstraint + + +class DqContTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate a dq-domain continuous control set torque controlled permanent magnet synchr. motor. + + Key: + ``'DqCont-TC-PMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.ContB6BridgeConverter` + - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Load: :py:class:`.ConstantSpeedLoad` + - Ode-Solver: :py:class:`.EulerSolver` + - Noise: **None** + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'torque' = 1.0`` + + - Visualization: :py:class:`.MotorDashboard` torque and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + + State Variables: + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` + + Reference Variables: + ``['torque']`` + + Control Cycle Time: + tau = 1e-4 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=13 * [-1], high=13 * [1]) + + Reference Space: + Box(low=[-1], high=[1]) + + Action Space: + Box(low=[-1, -1], high=[1, 1]) + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> + >>> # Select a different ode_solver with default parameters by passing a keystring + >>> my_overridden_solver = 'scipy.solve_ivp' + >>> + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='torque', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... 'DqCont-TC-PMSM-v0', + ... voltage_supply=my_changed_voltage_supply_args, + ... ode_solver=my_overridden_solver, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> done = True + >>> for _ in range(1000): + >>> if done: + >>> state, reference = env.reset() + >>> env.render() + >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) + """ + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + physical_system = SynchronousMotorSystem( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), + converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), + motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), + load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), + calc_jacobian=calc_jacobian, + tau=tau, + control_space='dq' + ) + reference_generator = initialize( + ReferenceGenerator, + reference_generator, + WienerProcessReferenceGenerator, + dict(reference_state='torque') + ) + reward_function = initialize( + RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(torque=1.0)) + ) + visualization = initialize( + ElectricMotorVisualization, + visualization, + MotorDashboard, + dict(state_plots=('torque',), action_plots='all') + ) + super().__init__( + physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + ) diff --git a/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py new file mode 100644 index 00000000..946e1834 --- /dev/null +++ b/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py @@ -0,0 +1,159 @@ +from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ + ElectricMotorVisualization +from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem +from gym_electric_motor.visualization import MotorDashboard +from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.constraints import SquaredConstraint + + +class FiniteCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate a finite control set current controlled permanent magnet synchr. motor. + + Key: + ``'Finite-CC-PMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.FiniteB6BridgeConverter` + - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Load: :py:class:`.ConstantSpeedLoad` + - Ode-Solver: :py:class:`.EulerSolver` + - Noise: **None** + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'i_sd' = 0.5, 'i_sq' = 0.5`` + + - Visualization: :py:class:`.MotorDashboard` current and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + + State Variables: + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` + + Reference Variables: + ``['i_sd', 'i_sq']`` + + Control Cycle Time: + tau = 1e-5 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=13 * [-1], high=13 * [1]) + + Reference Space: + Box(low=[-1, -1], high=[1, 1]) + + Action Space: + Discrete(8) + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> + >>> # Select a different ode_solver with default parameters by passing a keystring + >>> my_overridden_solver = 'scipy.solve_ivp' + >>> + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='i_sq', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... 'Finite-CC-PMSM-v0', + ... voltage_supply=my_changed_voltage_supply_args, + ... ode_solver=my_overridden_solver, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> done = True + >>> for _ in range(1000): + >>> if done: + >>> state, reference = env.reset() + >>> env.render() + >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) + """ + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + default_subgenerators = ( + WienerProcessReferenceGenerator(reference_state='i_sd'), + WienerProcessReferenceGenerator(reference_state='i_sq') + ) + + physical_system = SynchronousMotorSystem( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), + converter=initialize(ps.PowerElectronicConverter, converter, ps.FiniteB6BridgeConverter, dict()), + motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), + load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), + calc_jacobian=calc_jacobian, + tau=tau + ) + reference_generator = initialize( + ReferenceGenerator, + reference_generator, + MultipleReferenceGenerator, + dict(sub_generators=default_subgenerators) + ) + reward_function = initialize( + RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) + ) + visualization = initialize( + ElectricMotorVisualization, + visualization, + MotorDashboard, + dict(state_plots=('i_sd', 'i_sq'), action_plots='all') + ) + super().__init__( + physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + ) diff --git a/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py new file mode 100644 index 00000000..46b0c63a --- /dev/null +++ b/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py @@ -0,0 +1,149 @@ +from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ + ElectricMotorVisualization +from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem +from gym_electric_motor.visualization import MotorDashboard +from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.constraints import SquaredConstraint + + +class FiniteSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate a finite control set speed controlled permanent magnet synchr. motor. + + Key: + ``'Finite-SC-PMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.FiniteB6BridgeConverter` + - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Load: :py:class:`.PolynomialStaticLoad` + - Ode-Solver: :py:class:`.EulerSolver` + - Noise: **None** + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'omega' = 1.0`` + + - Visualization: :py:class:`.MotorDashboard` current and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + + State Variables: + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` + + Reference Variables: + ``['omega']`` + + Control Cycle Time: + tau = 1e-5 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=13 * [-1], high=13 * [1]) + + Reference Space: + Box(low=[-1], high=[1]) + + Action Space: + Discrete(8) + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> + >>> # Select a different ode_solver with default parameters by passing a keystring + >>> my_overridden_solver = 'scipy.solve_ivp' + >>> + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='omega', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... 'Finite-SC-PMSM-v0', + ... voltage_supply=my_changed_voltage_supply_args, + ... ode_solver=my_overridden_solver, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> done = True + >>> for _ in range(1000): + >>> if done: + >>> state, reference = env.reset() + >>> env.render() + >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) + """ + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + physical_system = SynchronousMotorSystem( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), + converter=initialize(ps.PowerElectronicConverter, converter, ps.FiniteB6BridgeConverter, dict()), + motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), + load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict( + load_parameter=dict(a=0.01, b=0.01, c=0.0) + )), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), + calc_jacobian=calc_jacobian, + tau=tau + ) + reference_generator = initialize( + ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, dict(reference_state='omega') + ) + reward_function = initialize( + RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(omega=1.0)) + ) + visualization = initialize( + ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) + super().__init__( + physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + ) diff --git a/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py new file mode 100644 index 00000000..113c5350 --- /dev/null +++ b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py @@ -0,0 +1,148 @@ +from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ + ElectricMotorVisualization +from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem +from gym_electric_motor.visualization import MotorDashboard +from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.constraints import SquaredConstraint + + +class FiniteTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate a finite control set torque controlled permanent magnet synchr. motor. + + Key: + ``'Finite-TC-PMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.FiniteB6BridgeConverter` + - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Load: :py:class:`.ConstantSpeedLoad` + - Ode-Solver: :py:class:`.EulerSolver` + - Noise: **None** + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'torque' = 1.0`` + + - Visualization: :py:class:`.MotorDashboard` torque and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + + State Variables: + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` + + Reference Variables: + ``['torque']`` + + Control Cycle Time: + tau = 1e-5 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=13 * [-1], high=13 * [1]) + + Reference Space: + Box(low=[-1], high=[1]) + + Action Space: + Discrete(8) + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> + >>> # Select a different ode_solver with default parameters by passing a keystring + >>> my_overridden_solver = 'scipy.solve_ivp' + >>> + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='torque', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... 'Finite-TC-PMSM-v0', + ... voltage_supply=my_changed_voltage_supply_args, + ... ode_solver=my_overridden_solver, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> done = True + >>> for _ in range(1000): + >>> if done: + >>> state, reference = env.reset() + >>> env.render() + >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) + """ + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), + constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + physical_system = SynchronousMotorSystem( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), + converter=initialize(ps.PowerElectronicConverter, converter, ps.FiniteB6BridgeConverter, dict()), + motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), + load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), + calc_jacobian=calc_jacobian, + tau=tau + ) + reference_generator = initialize( + ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, dict(reference_state='torque') + ) + reward_function = initialize( + RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(torque=1.0)) + ) + visualization = initialize( + ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('torque',), action_plots='all') + ) + super().__init__( + physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, + constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks + ) diff --git a/gym_electric_motor/physical_systems/__init__.py b/gym_electric_motor/physical_systems/__init__.py index 486fa042..91667630 100644 --- a/gym_electric_motor/physical_systems/__init__.py +++ b/gym_electric_motor/physical_systems/__init__.py @@ -1,11 +1,12 @@ -from .physical_systems import DcMotorSystem, SynchronousMotorSystem, SquirrelCageInductionMotorSystem, DoublyFedInductionMotorSystem +from .physical_systems import DcMotorSystem, SynchronousMotorSystem, SquirrelCageInductionMotorSystem, DoublyFedInductionMotorSystem, \ + ExternallyExcitedSynchronousMotorSystem from .converters import PowerElectronicConverter, FiniteOneQuadrantConverter, FiniteTwoQuadrantConverter, \ FiniteFourQuadrantConverter, FiniteMultiConverter, FiniteB6BridgeConverter, ContOneQuadrantConverter, \ ContTwoQuadrantConverter, ContFourQuadrantConverter, ContMultiConverter, ContB6BridgeConverter, NoConverter from .electric_motors import DcExternallyExcitedMotor, DcSeriesMotor, DcPermanentlyExcitedMotor, DcShuntMotor, \ PermanentMagnetSynchronousMotor, ElectricMotor, SynchronousReluctanceMotor, SquirrelCageInductionMotor, \ - DoublyFedInductionMotor + DoublyFedInductionMotor, ExternallyExcitedSynchronousMotor from .mechanical_loads import MechanicalLoad, PolynomialStaticLoad, ExternalSpeedLoad, ConstantSpeedLoad, \ OrnsteinUhlenbeckLoad @@ -61,10 +62,12 @@ register_class(DcExternallyExcitedMotor, ElectricMotor, 'DcExtEx') register_class(DcShuntMotor, ElectricMotor, 'DcShunt') register_class(PermanentMagnetSynchronousMotor, ElectricMotor, 'PMSM') +register_class(ExternallyExcitedSynchronousMotor, ElectricMotor, 'EESM') register_class(SynchronousReluctanceMotor, ElectricMotor, 'SynRM') register_class(SquirrelCageInductionMotor, ElectricMotor, 'SCIM') register_class(DoublyFedInductionMotor, ElectricMotor, 'DFIM') + register_class(IdealVoltageSupply, VoltageSupply, 'IdealVoltageSupply') register_class(RCVoltageSupply, VoltageSupply, 'RCVoltageSupply') register_class(AC1PhaseSupply, VoltageSupply, 'AC1PhaseSupply') diff --git a/gym_electric_motor/physical_systems/electric_motors/__init__.py b/gym_electric_motor/physical_systems/electric_motors/__init__.py index 5b94f2ad..81a09a24 100644 --- a/gym_electric_motor/physical_systems/electric_motors/__init__.py +++ b/gym_electric_motor/physical_systems/electric_motors/__init__.py @@ -15,6 +15,7 @@ from .synchronous_motor import SynchronousMotor from .synchronous_reluctance_motor import SynchronousReluctanceMotor from .permanent_magnet_synchronous_motor import PermanentMagnetSynchronousMotor +from .externally_excited_synchronous_motor import ExternallyExcitedSynchronousMotor # Induction Motors from .induction_motor import InductionMotor diff --git a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py index bd3be288..1cfe3563 100644 --- a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py @@ -98,8 +98,8 @@ class ExternallyExcitedSynchronousMotor(SynchronousMotor): 'p': 3, 'l_d': 1.66e-3, 'l_q': 0.35e-3, - 'l_m': 000, - 'l_e': 000, + 'l_m': 1.66e-4, + 'l_e': 1.66e-3, 'j_rotor': 0.3883, 'r_s': 15.55e-3, 'r_e': 7.2e-3, @@ -141,7 +141,7 @@ def _torque_limit(self): return self.torque([0, self._limits['i_sq'], self._limits['i_e'], 0]) else: i_n = self.nominal_values['i'] - _p = mp['psi_p'] / (2 * (mp['l_d'] - mp['l_q'])) + _p = mp['l_m'] * i_n / (2 * (mp['l_d'] - mp['l_q'])) _q = - i_n ** 2 / 2 i_d_opt = - _p / 2 - np.sqrt( (_p / 2) ** 2 - _q) i_q_opt = np.sqrt(i_n ** 2 - i_d_opt ** 2) diff --git a/gym_electric_motor/physical_systems/physical_systems.py b/gym_electric_motor/physical_systems/physical_systems.py index 34501205..0319d779 100644 --- a/gym_electric_motor/physical_systems/physical_systems.py +++ b/gym_electric_motor/physical_systems/physical_systems.py @@ -620,7 +620,7 @@ def simulate(self, action, *_, **__): if self.control_space == 'dq': action = self.dq_to_abc_space(action, eps) i_in_dq = self._electrical_motor.i_in(ode_state[self._ode_currents_idx]) - i_in_abc = list(self.dq_to_abc_space(i_in_dq[:2], eps)) + i_in_dq[2:] + i_in_abc = list(self.dq_to_abc_space(i_in_dq[:2], eps)) + list(i_in_dq[2:]) switching_times = self._converter.set_action(action, self._t) for t in switching_times[:-1]: @@ -681,7 +681,7 @@ def reset(self, *_): eps -= 2 * np.pi u_abc = self.converter.reset() u_abc = [u * u_s for u in u_abc for u_s in u_sup] - u_dq = self.abc_to_dq_space(u_abc, eps) + u_dq = self.abc_to_dq_space(u_abc[:3], eps) i_dq = ode_state[self._ode_currents_idx] i_abc = self.dq_to_abc_space(i_dq[:2], eps) torque = self.electrical_motor.torque(motor_state) From b6fa132b3d71c8fabef95ceca72faeede00d7e6d Mon Sep 17 00:00:00 2001 From: Arne Date: Fri, 11 Feb 2022 16:02:03 +0100 Subject: [PATCH 37/79] First running EESM Env --- .../envs/gym_eesm/abccont_cc_eesm_env.py | 7 ++-- .../envs/gym_eesm/abccont_sc_eesm_env.py | 2 +- .../externally_excited_synchronous_motor.py | 25 +++++++++++++++ .../physical_systems/physical_systems.py | 32 +++++++++---------- 4 files changed, 46 insertions(+), 20 deletions(-) diff --git a/gym_electric_motor/envs/gym_eesm/abccont_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/abccont_cc_eesm_env.py index 7f4d2137..f15fd57e 100644 --- a/gym_electric_motor/envs/gym_eesm/abccont_cc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/abccont_cc_eesm_env.py @@ -125,7 +125,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve """ default_subgenerators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), - WienerProcessReferenceGenerator(reference_state='i_sq') + WienerProcessReferenceGenerator(reference_state='i_sq'), + WienerProcessReferenceGenerator(reference_state='i_e'), ) default_subconverters = ( ps.ContB6BridgeConverter(), @@ -154,13 +155,13 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve dict(sub_generators=default_subgenerators) ) reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) + RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=1/3, i_sq=1/3, i_e=1/3)) ) visualization = initialize( ElectricMotorVisualization, visualization, MotorDashboard, - dict(state_plots=('i_sd', 'i_sq'), action_plots='all') + dict(state_plots=('i_sd', 'i_sq', 'i_e'), action_plots='all') ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, diff --git a/gym_electric_motor/envs/gym_eesm/abccont_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/abccont_sc_eesm_env.py index 85c8b012..6114cbce 100644 --- a/gym_electric_motor/envs/gym_eesm/abccont_sc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/abccont_sc_eesm_env.py @@ -73,7 +73,7 @@ class AbcContSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-SC-PMSM-v0', + ... 'AbcCont-SC-EESM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance diff --git a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py index 1cfe3563..d8aca024 100644 --- a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py @@ -133,6 +133,31 @@ def _update_model(self): self._model_constants[self.I_SQ_IDX] = self._model_constants[self.I_SQ_IDX] / mp['l_q'] self._model_constants[self.I_E_IDX] = self._model_constants[self.I_E_IDX] / mp['l_e'] + def electrical_ode(self, state, u_dq, omega, *_): + """ + The differential equation of the Synchronous Motor. + + Args: + state: The current state of the motor. [i_sd, i_sq, epsilon] + omega: The mechanical load + u_qd: The input voltages [u_sd, u_sq] + + Returns: + The derivatives of the state vector d/dt([i_sd, i_sq, epsilon]) + """ + return np.matmul(self._model_constants, np.array([ + omega, + state[self.I_SD_IDX], + state[self.I_SQ_IDX], + state[self.I_E_IDX], + u_dq[0], + u_dq[1], + u_dq[2], + omega * state[self.I_SD_IDX], + omega * state[self.I_SQ_IDX], + omega * state[self.I_E_IDX] + ])) + def _torque_limit(self): # Docstring of superclass mp = self._motor_parameter diff --git a/gym_electric_motor/physical_systems/physical_systems.py b/gym_electric_motor/physical_systems/physical_systems.py index 0319d779..a216c34b 100644 --- a/gym_electric_motor/physical_systems/physical_systems.py +++ b/gym_electric_motor/physical_systems/physical_systems.py @@ -619,37 +619,37 @@ def simulate(self, action, *_, **__): eps = ode_state[self._ode_epsilon_idx] if self.control_space == 'dq': action = self.dq_to_abc_space(action, eps) - i_in_dq = self._electrical_motor.i_in(ode_state[self._ode_currents_idx]) - i_in_abc = list(self.dq_to_abc_space(i_in_dq[:2], eps)) + list(i_in_dq[2:]) + i_in_dq_e = self._electrical_motor.i_in(ode_state[self._ode_currents_idx]) + i_in_abc_e = list(self.dq_to_abc_space(i_in_dq_e[:2], eps)) + list(i_in_dq_e[2:]) switching_times = self._converter.set_action(action, self._t) for t in switching_times[:-1]: - i_sup = self._converter.i_sup(i_in_abc) + i_sup = self._converter.i_sup(i_in_abc_e) u_sup = self._supply.get_voltage(self._t, i_sup) - u_in = self._converter.convert(i_in_abc, self._ode_solver.t) + u_in = self._converter.convert(i_in_abc_e, self._ode_solver.t) u_in = [u * u_s for u in u_in for u_s in u_sup] - u_dq = list(self.abc_to_dq_space(u_in[:2], eps)) + u_in[2:] + u_dq_e = list(self.abc_to_dq_space(u_in[:2], eps)) + u_in[2:] self._ode_solver.set_f_params(u_dq) ode_state = self._ode_solver.integrate(t) eps = ode_state[self._ode_epsilon_idx] - i_in_dq = self._electrical_motor.i_in(ode_state[self._ode_currents_idx]) - i_in_abc = list(self.dq_to_abc_space(i_in_dq[:2], eps)) + i_in_dq[2:] - - i_sup = self._converter.i_sup(i_in_abc) + i_in_dq_e = self._electrical_motor.i_in(ode_state[self._ode_currents_idx]) + i_in_abc_e = list(self.dq_to_abc_space(i_in_dq_e[:2], eps)) + i_in_dq_e[2:] + + i_sup = self._converter.i_sup(i_in_abc_e) u_sup = self._supply.get_voltage(self._t, i_sup) - u_in = self._converter.convert(i_in_abc, self._ode_solver.t) + u_in = self._converter.convert(i_in_abc_e, self._ode_solver.t) u_in = [u * u_s for u in u_in for u_s in u_sup] - u_dq = self.abc_to_dq_space(u_in[:2], eps) + u_in[2:] - self._ode_solver.set_f_params(u_dq) + u_dq_e = np.concatenate((self.abc_to_dq_space(u_in[:3], eps), u_in[3:])) + self._ode_solver.set_f_params(u_dq_e) ode_state = self._ode_solver.integrate(self._t + self._tau) self._t = self._ode_solver.t self._k += 1 torque = self._electrical_motor.torque(ode_state[self._motor_ode_idx]) noise = self._noise_generator.noise() mechanical_state = ode_state[self._load_ode_idx] - i_dq = ode_state[self._ode_currents_idx] + i_dq_e = ode_state[self._ode_currents_idx] i_abc = list( - self.dq_to_abc_space(i_dq, eps) + self.dq_to_abc_space(i_dq_e[:2], eps) ) eps = ode_state[self._ode_epsilon_idx] % (2 * np.pi) if eps > np.pi: @@ -658,8 +658,8 @@ def simulate(self, action, *_, **__): system_state = np.concatenate(( mechanical_state, [torque], - i_abc, i_dq, - u_in, u_dq, + i_abc, i_dq_e, + u_in[:3], u_dq_e, [eps], u_sup )) From 01f3ff30fcbf7209e221ad9bc542108e57e26372 Mon Sep 17 00:00:00 2001 From: Arne Date: Sat, 12 Feb 2022 12:44:38 +0100 Subject: [PATCH 38/79] Bugfix --- gym_electric_motor/physical_systems/physical_systems.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym_electric_motor/physical_systems/physical_systems.py b/gym_electric_motor/physical_systems/physical_systems.py index a216c34b..19faae51 100644 --- a/gym_electric_motor/physical_systems/physical_systems.py +++ b/gym_electric_motor/physical_systems/physical_systems.py @@ -629,7 +629,7 @@ def simulate(self, action, *_, **__): u_in = self._converter.convert(i_in_abc_e, self._ode_solver.t) u_in = [u * u_s for u in u_in for u_s in u_sup] u_dq_e = list(self.abc_to_dq_space(u_in[:2], eps)) + u_in[2:] - self._ode_solver.set_f_params(u_dq) + self._ode_solver.set_f_params(u_dq_e) ode_state = self._ode_solver.integrate(t) eps = ode_state[self._ode_epsilon_idx] i_in_dq_e = self._electrical_motor.i_in(ode_state[self._ode_currents_idx]) From ee473423fbfef2f08f37f47d29363ad77153a885 Mon Sep 17 00:00:00 2001 From: Arne Date: Sat, 12 Feb 2022 18:36:21 +0100 Subject: [PATCH 39/79] Performance improvements --- .../mechanical_loads/constant_speed_load.py | 8 ++++--- .../polynomial_static_load.py | 15 +++++++++++-- .../physical_systems/physical_systems.py | 21 +++++++++++-------- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/gym_electric_motor/physical_systems/mechanical_loads/constant_speed_load.py b/gym_electric_motor/physical_systems/mechanical_loads/constant_speed_load.py index 034b739a..965ff10f 100644 --- a/gym_electric_motor/physical_systems/mechanical_loads/constant_speed_load.py +++ b/gym_electric_motor/physical_systems/mechanical_loads/constant_speed_load.py @@ -32,13 +32,15 @@ def __init__(self, omega_fixed=0, load_initializer=None, **kwargs): """ super().__init__(load_initializer=load_initializer, **kwargs) self._omega = omega_fixed or self._initializer['states']['omega'] - if omega_fixed: + if omega_fixed != 0: self._initializer['states']['omega'] = omega_fixed + self._ode_result = np.array([0.]) + self._jacobian_result = (np.array([[0.]]), np.array([0.])) def mechanical_ode(self, *_, **__): # Docstring of superclass - return np.array([0]) + return self._ode_result def mechanical_jacobian(self, t, mechanical_state, torque): # Docstring of superclass - return np.array([[0]]), np.array([0]) + return self._jacobian_result diff --git a/gym_electric_motor/physical_systems/mechanical_loads/polynomial_static_load.py b/gym_electric_motor/physical_systems/mechanical_loads/polynomial_static_load.py index f1fb1fbf..5a6bb351 100644 --- a/gym_electric_motor/physical_systems/mechanical_loads/polynomial_static_load.py +++ b/gym_electric_motor/physical_systems/mechanical_loads/polynomial_static_load.py @@ -59,6 +59,13 @@ def load_parameter(self): """ return self._load_parameter + def set_j_rotor(self, j_rotor): + # Docstring of superclass + super().set_j_rotor(j_rotor) + self._omega_linear_factor = self._j_total / self.tau_decay + self._omega_lim = self._a / self._j_total * self.tau_decay + + def __init__(self, load_parameter=None, limits=None, load_initializer=None): """ Args: @@ -73,14 +80,18 @@ def __init__(self, load_parameter=None, limits=None, load_initializer=None): self._a = self._load_parameter['a'] self._b = self._load_parameter['b'] self._c = self._load_parameter['c'] + # Speed value at which the linear behavior switches to constant + self._omega_lim = self._a / self._j_total * self.tau_decay + # Slope for the linear growth of the constant load part around zero speed + self._omega_linear_factor = self._j_total / self.tau_decay def _static_load(self, omega): """Calculation of the load torque for a given speed omega.""" sign = 1 if omega > 0 else -1 if omega < -0 else 0 # Limit the constant load term 'a' for velocities around zero for a more stable integration a = sign * self._a \ - if abs(omega) > self._a / self._j_total * self.tau_decay \ - else self._j_total / self.tau_decay * omega + if abs(omega) > self._omega_lim \ + else self._omega_linear_factor * omega return sign * self._c * omega**2 + self._b * omega + a def mechanical_ode(self, t, mechanical_state, torque): diff --git a/gym_electric_motor/physical_systems/physical_systems.py b/gym_electric_motor/physical_systems/physical_systems.py index 19faae51..5b07eff9 100644 --- a/gym_electric_motor/physical_systems/physical_systems.py +++ b/gym_electric_motor/physical_systems/physical_systems.py @@ -214,35 +214,38 @@ def _system_equation(self, t, state, u_in, **__): ndarray(float): The derivatives of the ODE-State. Based on this, the Ode Solver calculates the next state. """ if self._system_eq_placeholder is None: - + motor_state = state[self._motor_ode_idx] motor_derivative = self._electrical_motor.electrical_ode( - state[self._motor_ode_idx], u_in, state[self._omega_ode_idx] + motor_state, u_in, state[self._omega_ode_idx] + ) + torque = self._electrical_motor.torque(motor_state) + load_derivative = self._mechanical_load.mechanical_ode( + t, state[self._load_ode_idx], torque ) - torque = self._electrical_motor.torque(state[self._motor_ode_idx]) - load_derivative = self._mechanical_load.mechanical_ode(t, state[ - self._load_ode_idx], torque) self._system_eq_placeholder = np.concatenate((load_derivative, motor_derivative)) self._motor_deriv_size = motor_derivative.size self._load_deriv_size = load_derivative.size else: + motor_state = state[self._motor_ode_idx] self._system_eq_placeholder[:self._load_deriv_size] = \ self._mechanical_load.mechanical_ode( t, state[self._load_ode_idx], - self._electrical_motor.torque(state[self._motor_ode_idx]) + self._electrical_motor.torque(motor_state) ).ravel() self._system_eq_placeholder[self._load_deriv_size:] = \ self._electrical_motor.electrical_ode( - state[self._motor_ode_idx], u_in, state[self._omega_ode_idx] + motor_state, u_in, state[self._omega_ode_idx] ).ravel() return self._system_eq_placeholder def _system_jacobian(self, t, state, u_in, **__): + motor_state = state[self._motor_ode_idx] motor_jac, el_state_over_omega, torque_over_el_state = self._electrical_motor.electrical_jacobian( - state[self._motor_ode_idx], u_in, state[self._omega_ode_idx] + motor_state, u_in, state[self._omega_ode_idx] ) - torque = self._electrical_motor.torque(state[self._motor_ode_idx]) + torque = self._electrical_motor.torque(motor_state) load_jac, load_over_torque = self._mechanical_load.mechanical_jacobian( t, state[self._load_ode_idx], torque ) From 5d0d031701ab7c1b9a525eb19813369b03053043 Mon Sep 17 00:00:00 2001 From: Arne Date: Sat, 12 Feb 2022 18:57:26 +0100 Subject: [PATCH 40/79] Corrected documentation of motors --- .../physical_systems/electric_motors/dc_motor.py | 12 ++++++------ .../dc_permanently_excited_motor.py | 9 +++++---- .../electric_motors/dc_series_motor.py | 14 +++++++------- .../electric_motors/dc_shunt_motor.py | 13 +++++++------ .../electric_motors/doubly_fed_induction_motor.py | 13 +++++++------ .../externally_excited_synchronous_motor.py | 4 ++-- .../permanent_magnet_synchronous_motor.py | 12 ++++++------ .../synchronous_reluctance_motor.py | 10 +++++----- 8 files changed, 45 insertions(+), 42 deletions(-) diff --git a/gym_electric_motor/physical_systems/electric_motors/dc_motor.py b/gym_electric_motor/physical_systems/electric_motors/dc_motor.py index 728c0b43..401c9264 100644 --- a/gym_electric_motor/physical_systems/electric_motors/dc_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/dc_motor.py @@ -13,12 +13,12 @@ class DcMotor(ElectricMotor): ===================== ========== ============= =========================================== Motor Parameter Unit Default Value Description ===================== ========== ============= =========================================== - r_a Ohm 0.78 Armature circuit resistance - r_e Ohm 25 Exciting circuit resistance - l_a H 6.3e-3 Armature circuit inductance - l_e H 1.2 Exciting circuit inductance - l_e_prime H 0.0094 Effective excitation inductance - j_rotor kg/m^2 0.017 Moment of inertia of the rotor + r_a Ohm 16e-3 Armature circuit resistance + r_e Ohm 16e-2 Exciting circuit resistance + l_a H 19e-6 Armature circuit inductance + l_e H 5.4e-3 Exciting circuit inductance + l_e_prime H 1.7e-3 Effective excitation inductance + j_rotor kg/m^2 0.025 Moment of inertia of the rotor ===================== ========== ============= =========================================== =============== ====== ============================================= diff --git a/gym_electric_motor/physical_systems/electric_motors/dc_permanently_excited_motor.py b/gym_electric_motor/physical_systems/electric_motors/dc_permanently_excited_motor.py index 1aa22a25..614c66aa 100644 --- a/gym_electric_motor/physical_systems/electric_motors/dc_permanently_excited_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/dc_permanently_excited_motor.py @@ -9,11 +9,12 @@ class DcPermanentlyExcitedMotor(DcMotor): ===================== ========== ============= =========================================== Motor Parameter Unit Default Value Description ===================== ========== ============= =========================================== - r_a Ohm 25.0 Armature circuit resistance - l_a H 3.438e-2 Armature circuit inductance - psi_e Wb 18 Magnetic Flux of the permanent magnet - j_rotor kg/m^2 0.017 Moment of inertia of the rotor + r_a Ohm 16e-3 Armature circuit resistance + l_a H 19e-6 Armature circuit inductance + psi_e Wb 0.165 Magnetic Flux of the permanent magnet + j_rotor kg/m^2 0.025 Moment of inertia of the rotor ===================== ========== ============= =========================================== + =============== ====== ============================================= Motor Currents Unit Description =============== ====== ============================================= diff --git a/gym_electric_motor/physical_systems/electric_motors/dc_series_motor.py b/gym_electric_motor/physical_systems/electric_motors/dc_series_motor.py index 0316cb10..a6cc3e82 100644 --- a/gym_electric_motor/physical_systems/electric_motors/dc_series_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/dc_series_motor.py @@ -9,14 +9,14 @@ class DcSeriesMotor(DcMotor): ===================== ========== ============= =========================================== Motor Parameter Unit Default Value Description ===================== ========== ============= =========================================== - r_a Ohm 2.78 Armature circuit resistance - r_e Ohm 1.0 Exciting circuit resistance - l_a H 6.3e-3 Armature circuit inductance - l_e H 1.6e-3 Exciting circuit inductance - l_e_prime H 0.05 Effective excitation inductance - j_rotor kg/m^2 0.017 Moment of inertia of the rotor + r_a Ohm 16e-3 Armature circuit resistance + r_e Ohm 48e-3 Exciting circuit resistance + l_a H 19e-6 Armature circuit inductance + l_e H 5.4e-3 Exciting circuit inductance + l_e_prime H 1.7e-3 Effective excitation inductance + j_rotor kg/m^2 0.025 Moment of inertia of the rotor ===================== ========== ============= =========================================== - + =============== ====== ============================================= Motor Currents Unit Description =============== ====== ============================================= diff --git a/gym_electric_motor/physical_systems/electric_motors/dc_shunt_motor.py b/gym_electric_motor/physical_systems/electric_motors/dc_shunt_motor.py index 28c6be19..15c4c7d6 100644 --- a/gym_electric_motor/physical_systems/electric_motors/dc_shunt_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/dc_shunt_motor.py @@ -9,12 +9,12 @@ class DcShuntMotor(DcMotor): ===================== ========== ============= =========================================== Motor Parameter Unit Default Value Description ===================== ========== ============= =========================================== - r_a Ohm 0.78 Armature circuit resistance - r_e Ohm 25 Exciting circuit resistance - l_a H 6.3e-3 Armature circuit inductance - l_e H 1.2 Exciting circuit inductance - l_e_prime H 0.0094 Effective excitation inductance - j_rotor kg/m^2 0.017 Moment of inertia of the rotor + r_a Ohm 16e-3 Armature circuit resistance + r_e Ohm 4e-1 Exciting circuit resistance + l_a H 19e-6 Armature circuit inductance + l_e H 5.4e-3 Exciting circuit inductance + l_e_prime H 1.7e-3 Effective excitation inductance + j_rotor kg/m^2 0.025 Moment of inertia of the rotor ===================== ========== ============= =========================================== =============== ====== ============================================= @@ -23,6 +23,7 @@ class DcShuntMotor(DcMotor): i_a A Armature circuit current i_e A Exciting circuit current =============== ====== ============================================= + =============== ====== ============================================= Motor Voltages Unit Description =============== ====== ============================================= diff --git a/gym_electric_motor/physical_systems/electric_motors/doubly_fed_induction_motor.py b/gym_electric_motor/physical_systems/electric_motors/doubly_fed_induction_motor.py index 1de21224..d76c49ae 100644 --- a/gym_electric_motor/physical_systems/electric_motors/doubly_fed_induction_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/doubly_fed_induction_motor.py @@ -9,14 +9,15 @@ class DoublyFedInductionMotor(InductionMotor): ===================== ========== ============= =========================================== Motor Parameter Unit Default Value Description ===================== ========== ============= =========================================== - r_s Ohm 12e-3 Stator resistance - r_r Ohm 21e-3 Rotor resistance - l_m H 13.5e-3 Main inductance - l_sigs H 0.2e-3 Stator-side stray inductance - l_sigr H 0.1e-3 Rotor-side stray inductance + r_s Ohm 4.42 Stator resistance + r_r Ohm 3.51 Rotor resistance + l_m H 297.5e-3 Main inductance + l_sigs H 25.71e-3 Stator-side stray inductance + l_sigr H 25.71e-3 Rotor-side stray inductance p 1 2 Pole pair number - j_rotor kg/m^2 1e3 Moment of inertia of the rotor + j_rotor kg/m^2 13.695e-3 Moment of inertia of the rotor ===================== ========== ============= =========================================== + =============== ====== ============================================= Motor Currents Unit Description =============== ====== ============================================= diff --git a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py index d8aca024..ca3f4355 100644 --- a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py @@ -11,9 +11,9 @@ class ExternallyExcitedSynchronousMotor(SynchronousMotor): ===================== ========== ============= =========================================== r_s mOhm 15.55 Stator resistance r_e mOhm 7.2 Excitation resistance - l_d mH 1.66 Direct axis inductance + l_d mH 0.166 Direct axis inductance l_q mH 0.35 Quadrature axis inductance - l_e mH 1.74 Excitation inductance + l_e mH 1.66 Excitation inductance p 1 3 Pole pair number j_rotor kg/m^2 0.3883 Moment of inertia of the rotor ===================== ========== ============= =========================================== diff --git a/gym_electric_motor/physical_systems/electric_motors/permanent_magnet_synchronous_motor.py b/gym_electric_motor/physical_systems/electric_motors/permanent_magnet_synchronous_motor.py index 2222b625..5aba0358 100644 --- a/gym_electric_motor/physical_systems/electric_motors/permanent_magnet_synchronous_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/permanent_magnet_synchronous_motor.py @@ -9,13 +9,13 @@ class PermanentMagnetSynchronousMotor(SynchronousMotor): ===================== ========== ============= =========================================== Motor Parameter Unit Default Value Description ===================== ========== ============= =========================================== - r_s Ohm 0.78 Stator resistance - l_d H 1.2 Direct axis inductance - l_q H 6.3e-3 Quadrature axis inductance - p 1 2 Pole pair number - j_rotor kg/m^2 0.017 Moment of inertia of the rotor + r_s Ohm 18e-3 Stator resistance + l_d H 0.37e-3 Direct axis inductance + l_q H 1.2e-3 Quadrature axis inductance + p 1 3 Pole pair number + j_rotor kg/m^2 0.03883 Moment of inertia of the rotor ===================== ========== ============= =========================================== - + =============== ====== ============================================= Motor Currents Unit Description =============== ====== ============================================= diff --git a/gym_electric_motor/physical_systems/electric_motors/synchronous_reluctance_motor.py b/gym_electric_motor/physical_systems/electric_motors/synchronous_reluctance_motor.py index 5e607b1a..273f4bd9 100644 --- a/gym_electric_motor/physical_systems/electric_motors/synchronous_reluctance_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/synchronous_reluctance_motor.py @@ -8,11 +8,11 @@ class SynchronousReluctanceMotor(SynchronousMotor): ===================== ========== ============= =========================================== Motor Parameter Unit Default Value Description ===================== ========== ============= =========================================== - r_s Ohm 0.78 Stator resistance - l_d H 1.2 Direct axis inductance - l_q H 6.3e-3 Quadrature axis inductance - p 1 2 Pole pair number - j_rotor kg/m^2 0.017 Moment of inertia of the rotor + r_s Ohm 0.57 Stator resistance + l_d H 10.1e-3 Direct axis inductance + l_q H 4.1e-3 Quadrature axis inductance + p 1 4 Pole pair number + j_rotor kg/m^2 0.8e-3 Moment of inertia of the rotor ===================== ========== ============= =========================================== =============== ====== ============================================= From 3439232d1216affce9d67814a9e2ffed1eed7af7 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 14 Feb 2022 16:46:20 +0100 Subject: [PATCH 41/79] add newline at end of file --- .../state_action_processors/dq_to_abc_action_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py index e505b6d8..f7bc7a47 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -144,4 +144,4 @@ def simulate(self, action): def set_physical_system(self, physical_system): super().set_physical_system(physical_system) self._flux_angle_index = physical_system.state_names.index('psi_angle') - return self \ No newline at end of file + return self From 220f165c2dc53db775118d0b35d5b3d8175bb789 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 14 Feb 2022 18:45:05 +0100 Subject: [PATCH 42/79] example fixes --- .../classic_controllers_ind_motor_example.py | 2 +- .../classic_controllers_synch_motor_example.py | 2 +- .../custom_classic_controllers_ind_motor_example.py | 2 +- .../custom_classic_controllers_synch_motor_example.py | 4 ++-- examples/environment_features/scim_ideal_grid_simulation.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/classic_controllers/classic_controllers_ind_motor_example.py b/examples/classic_controllers/classic_controllers_ind_motor_example.py index 13aebbc1..04803219 100644 --- a/examples/classic_controllers/classic_controllers_ind_motor_example.py +++ b/examples/classic_controllers/classic_controllers_ind_motor_example.py @@ -20,7 +20,7 @@ motor_type = 'SCIM' control_type = 'TC' - action_type = 'AbcCont' + action_type = 'Cont' env_id = action_type + '-' + control_type + '-' + motor_type + '-v0' diff --git a/examples/classic_controllers/classic_controllers_synch_motor_example.py b/examples/classic_controllers/classic_controllers_synch_motor_example.py index 091c5721..3af8f8c8 100644 --- a/examples/classic_controllers/classic_controllers_synch_motor_example.py +++ b/examples/classic_controllers/classic_controllers_synch_motor_example.py @@ -14,7 +14,7 @@ 'TC' Torque Control 'CC' Current Control - action_type: 'AbcCont' Continuous Action Space in ABC-Coordinates + action_type: 'Cont' Continuous Action Space in ABC-Coordinates 'Finite' Discrete Action Space """ diff --git a/examples/classic_controllers/custom_classic_controllers_ind_motor_example.py b/examples/classic_controllers/custom_classic_controllers_ind_motor_example.py index bd483320..a09b6835 100644 --- a/examples/classic_controllers/custom_classic_controllers_ind_motor_example.py +++ b/examples/classic_controllers/custom_classic_controllers_ind_motor_example.py @@ -18,7 +18,7 @@ motor_type = 'SCIM' control_type = 'SC' - action_type = 'AbcCont' + action_type = 'Cont' env_id = action_type + '-' + control_type + '-' + motor_type + '-v0' diff --git a/examples/classic_controllers/custom_classic_controllers_synch_motor_example.py b/examples/classic_controllers/custom_classic_controllers_synch_motor_example.py index dcff1c62..d9881a94 100644 --- a/examples/classic_controllers/custom_classic_controllers_synch_motor_example.py +++ b/examples/classic_controllers/custom_classic_controllers_synch_motor_example.py @@ -14,13 +14,13 @@ 'TC' Torque Control 'CC' Current Control - action_type: 'AbcCont' Continuous Action Space in ABC-Coordinates + action_type: 'Cont' Continuous Action Space in ABC-Coordinates 'Finite' Discrete Action Space """ motor_type = 'PMSM' control_type = 'SC' - action_type = 'AbcCont' + action_type = 'Cont' env_id = action_type + '-' + control_type + '-' + motor_type + '-v0' diff --git a/examples/environment_features/scim_ideal_grid_simulation.py b/examples/environment_features/scim_ideal_grid_simulation.py index 90abb992..0c46bc11 100644 --- a/examples/environment_features/scim_ideal_grid_simulation.py +++ b/examples/environment_features/scim_ideal_grid_simulation.py @@ -34,7 +34,7 @@ def grid_voltage(t): # Create the environment env = gem.make( # Choose the squirrel cage induction motor (SCIM) with continuous-control-set - "AbcCont-CC-SCIM-v0", + "Cont-CC-SCIM-v0", # Define the numerical solver for the simulation ode_solver="scipy.ode", From 94d84f309fd4c601942e040af1e4fa5201ed9c63 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 14 Feb 2022 19:30:25 +0100 Subject: [PATCH 43/79] example fix --- .../pmsm_mpc_dq_current_control.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb b/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb index 5fbf778c..a7222e84 100644 --- a/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb +++ b/examples/model_predictive_controllers/pmsm_mpc_dq_current_control.ipynb @@ -344,7 +344,7 @@ "source": [ "%matplotlib notebook\n", "visu = MotorDashboard(state_plots=['i_sq', 'i_sd', 'u_sq', 'u_sd'], update_interval=10) # visualization\n", - "env = gem.make('AbcCont-CC-PMSM-v0',\n", + "env = gem.make('Cont-CC-PMSM-v0',\n", " visualization=visu,\n", " load=ConstantSpeedLoad(omega_fixed=1000 * np.pi / 30), # Fixed preset speed\n", " ode_solver='scipy.solve_ivp', # ODE Solver of the simulation\n", From cf71a4ecc67c00830501420e7c4f87e5c7aea540 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 14 Feb 2022 19:32:57 +0100 Subject: [PATCH 44/79] merge fix --- gym_electric_motor/physical_systems/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gym_electric_motor/physical_systems/__init__.py b/gym_electric_motor/physical_systems/__init__.py index 84ad2ff9..b91b743a 100644 --- a/gym_electric_motor/physical_systems/__init__.py +++ b/gym_electric_motor/physical_systems/__init__.py @@ -1,14 +1,14 @@ from .physical_systems import DcMotorSystem, SynchronousMotorSystem, SquirrelCageInductionMotorSystem, DoublyFedInductionMotorSystem, \ ExternallyExcitedSynchronousMotorSystem, ThreePhaseMotorSystem, SCMLSystem - + from .converters import PowerElectronicConverter, FiniteOneQuadrantConverter, FiniteTwoQuadrantConverter, \ FiniteFourQuadrantConverter, FiniteMultiConverter, FiniteB6BridgeConverter, ContOneQuadrantConverter, \ ContTwoQuadrantConverter, ContFourQuadrantConverter, ContMultiConverter, ContB6BridgeConverter, NoConverter from .electric_motors import DcExternallyExcitedMotor, DcSeriesMotor, DcPermanentlyExcitedMotor, DcShuntMotor, \ PermanentMagnetSynchronousMotor, ElectricMotor, SynchronousReluctanceMotor, SquirrelCageInductionMotor, \ - DoublyFedInductionMotor, ExternallyExcitedSynchronousMotor, , ThreePhaseMotor + DoublyFedInductionMotor, ExternallyExcitedSynchronousMotor, ThreePhaseMotor from .mechanical_loads import MechanicalLoad, PolynomialStaticLoad, ExternalSpeedLoad, ConstantSpeedLoad, \ From c1a4888fef04d0b63c3dda565c0146029e2605d6 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 14 Feb 2022 20:13:57 +0100 Subject: [PATCH 45/79] Environments finished --- gym_electric_motor/__init__.py | 28 +-- gym_electric_motor/envs/__init__.py | 9 +- gym_electric_motor/envs/gym_eesm/__init__.py | 9 +- .../envs/gym_eesm/abccont_tc_eesm_env.py | 155 ----------------- ...ont_cc_eesm_env.py => cont_cc_eesm_env.py} | 30 ++-- ...ont_sc_eesm_env.py => cont_sc_eesm_env.py} | 49 +++--- ...ont_tc_eesm_env.py => cont_tc_eesm_env.py} | 48 +++--- .../envs/gym_eesm/dqcont_cc_eesm_env.py | 160 ------------------ .../envs/gym_eesm/dqcont_sc_eesm_env.py | 150 ---------------- .../envs/gym_eesm/finite_cc_eesm_env.py | 53 +++--- .../envs/gym_eesm/finite_sc_eesm_env.py | 68 ++++---- .../envs/gym_eesm/finite_tc_eesm_env.py | 63 ++++--- .../physical_systems/physical_systems.py | 15 +- 13 files changed, 181 insertions(+), 656 deletions(-) delete mode 100644 gym_electric_motor/envs/gym_eesm/abccont_tc_eesm_env.py rename gym_electric_motor/envs/gym_eesm/{abccont_cc_eesm_env.py => cont_cc_eesm_env.py} (88%) rename gym_electric_motor/envs/gym_eesm/{abccont_sc_eesm_env.py => cont_sc_eesm_env.py} (82%) rename gym_electric_motor/envs/gym_eesm/{dqcont_tc_eesm_env.py => cont_tc_eesm_env.py} (83%) delete mode 100644 gym_electric_motor/envs/gym_eesm/dqcont_cc_eesm_env.py delete mode 100644 gym_electric_motor/envs/gym_eesm/dqcont_sc_eesm_env.py diff --git a/gym_electric_motor/__init__.py b/gym_electric_motor/__init__.py index 2d23c3e0..c74d2e85 100644 --- a/gym_electric_motor/__init__.py +++ b/gym_electric_motor/__init__.py @@ -206,36 +206,20 @@ **registration_kwargs ) register( - id='AbcCont-CC-EESM-v0', - entry_point=envs_path+'AbcContCurrentControlExternallyExcitedSynchronousMotorEnv', + id='Cont-CC-EESM-v0', + entry_point=envs_path+'ContCurrentControlExternallyExcitedSynchronousMotorEnv', **registration_kwargs ) register( - id='AbcCont-TC-EESM-v0', - entry_point=envs_path+'AbcContTorqueControlExternallyExcitedSynchronousMotorEnv', + id='Cont-TC-EESM-v0', + entry_point=envs_path+'ContTorqueControlExternallyExcitedSynchronousMotorEnv', **registration_kwargs ) register( - id='AbcCont-SC-EESM-v0', - entry_point=envs_path+'AbcContSpeedControlExternallyExcitedSynchronousMotorEnv', + id='Cont-SC-EESM-v0', + entry_point=envs_path+'ContSpeedControlExternallyExcitedSynchronousMotorEnv', **registration_kwargs ) -register( - id='DqCont-CC-EESM-v0', - entry_point=envs_path+'DqContCurrentControlExternallyExcitedSynchronousMotorEnv', - **registration_kwargs -) -register( - id='DqCont-TC-EESM-v0', - entry_point=envs_path+'DqContTorqueControlExternallyExcitedSynchronousMotorEnv', - **registration_kwargs -) -register( - id='DqCont-SC-EESM-v0', - entry_point=envs_path+'DqContSpeedControlExternallyExcitedSynchronousMotorEnv', - **registration_kwargs -) - # Synchronous Reluctance Motor Environments register( id='Finite-SC-SynRM-v0', diff --git a/gym_electric_motor/envs/__init__.py b/gym_electric_motor/envs/__init__.py index 7c1a7bbe..a7b5f921 100644 --- a/gym_electric_motor/envs/__init__.py +++ b/gym_electric_motor/envs/__init__.py @@ -37,12 +37,9 @@ from .gym_eesm.finite_sc_eesm_env import FiniteSpeedControlExternallyExcitedSynchronousMotorEnv from .gym_eesm.finite_cc_eesm_env import FiniteCurrentControlExternallyExcitedSynchronousMotorEnv from .gym_eesm.finite_tc_eesm_env import FiniteTorqueControlExternallyExcitedSynchronousMotorEnv -from .gym_eesm.abccont_cc_eesm_env import AbcContCurrentControlExternallyExcitedSynchronousMotorEnv -from .gym_eesm.abccont_sc_eesm_env import AbcContSpeedControlExternallyExcitedSynchronousMotorEnv -from .gym_eesm.abccont_tc_eesm_env import AbcContTorqueControlExternallyExcitedSynchronousMotorEnv -from .gym_eesm.dqcont_sc_eesm_env import DqContSpeedControlExternallyExcitedSynchronousMotorEnv -from .gym_eesm.dqcont_tc_eesm_env import DqContTorqueControlExternallyExcitedSynchronousMotorEnv -from .gym_eesm.dqcont_cc_eesm_env import DqContCurrentControlExternallyExcitedSynchronousMotorEnv +from .gym_eesm.cont_cc_eesm_env import ContCurrentControlExternallyExcitedSynchronousMotorEnv +from .gym_eesm.cont_sc_eesm_env import ContSpeedControlExternallyExcitedSynchronousMotorEnv +from .gym_eesm.cont_tc_eesm_env import ContTorqueControlExternallyExcitedSynchronousMotorEnv from .gym_synrm.finite_sc_synrm_env import FiniteSpeedControlSynchronousReluctanceMotorEnv from .gym_synrm.finite_cc_synrm_env import FiniteCurrentControlSynchronousReluctanceMotorEnv diff --git a/gym_electric_motor/envs/gym_eesm/__init__.py b/gym_electric_motor/envs/gym_eesm/__init__.py index 0a51000a..be7ece13 100644 --- a/gym_electric_motor/envs/gym_eesm/__init__.py +++ b/gym_electric_motor/envs/gym_eesm/__init__.py @@ -1,9 +1,6 @@ -from .dqcont_cc_eesm_env import DqContCurrentControlExternallyExcitedSynchronousMotorEnv -from .dqcont_tc_eesm_env import DqContTorqueControlExternallyExcitedSynchronousMotorEnv -from .dqcont_sc_eesm_env import DqContSpeedControlExternallyExcitedSynchronousMotorEnv -from .abccont_sc_eesm_env import AbcContSpeedControlExternallyExcitedSynchronousMotorEnv -from .abccont_tc_eesm_env import AbcContTorqueControlExternallyExcitedSynchronousMotorEnv -from .abccont_cc_eesm_env import AbcContCurrentControlExternallyExcitedSynchronousMotorEnv +from .cont_sc_eesm_env import ContSpeedControlExternallyExcitedSynchronousMotorEnv +from .cont_tc_eesm_env import ContTorqueControlExternallyExcitedSynchronousMotorEnv +from .cont_cc_eesm_env import ContCurrentControlExternallyExcitedSynchronousMotorEnv from .finite_tc_eesm_env import FiniteTorqueControlExternallyExcitedSynchronousMotorEnv from .finite_sc_eesm_env import FiniteSpeedControlExternallyExcitedSynchronousMotorEnv from .finite_cc_eesm_env import FiniteCurrentControlExternallyExcitedSynchronousMotorEnv diff --git a/gym_electric_motor/envs/gym_eesm/abccont_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/abccont_tc_eesm_env.py deleted file mode 100644 index d0c368ca..00000000 --- a/gym_electric_motor/envs/gym_eesm/abccont_tc_eesm_env.py +++ /dev/null @@ -1,155 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class AbcContTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a abc-domain continuous control set torque controlled permanent magnet synchr. motor. - - Key: - ``'AbcCont-TC-PMSM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.PermanentMagnetSynchronousMotor` - - Load: :py:class:`.ConstantSpeedLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'torque' = 1.0`` - - - Visualization: :py:class:`.MotorDashboard` torque and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` - - Reference Variables: - ``['torque']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=13 * [-1], high=13 * [1]) - - Reference Space: - Box(low=[-1, -1], high=[1, 1]) - - Action Space: - Box(low=[-1, -1, -1], high=[1, 1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='torque', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'AbcCont-TC-PMSM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - physical_system = SynchronousMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='abc' - ) - reference_generator = initialize( - ReferenceGenerator, - reference_generator, - WienerProcessReferenceGenerator, - dict(reference_state='torque') - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(torque=1.0)) - ) - visualization = initialize( - ElectricMotorVisualization, - visualization, - MotorDashboard, - dict(state_plots=('torque',), action_plots='all') - ) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks - ) diff --git a/gym_electric_motor/envs/gym_eesm/abccont_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py similarity index 88% rename from gym_electric_motor/envs/gym_eesm/abccont_cc_eesm_env.py rename to gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py index f15fd57e..285250db 100644 --- a/gym_electric_motor/envs/gym_eesm/abccont_cc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py @@ -6,24 +6,23 @@ from gym_electric_motor import physical_systems as ps from gym_electric_motor.reward_functions import WeightedSumOfErrors from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint +from gym_electric_motor.constraints import LimitConstraint, SquaredConstraint -class AbcContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): +class ContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): """ Description: - Environment to simulate a abc-domain continuous control set current controlled permanent magnet synchr. motor. + Environment to simulate a continuous control set current controlled externally excited synchronous motor. Key: - ``'AbcCont-CC-EESM-v0'`` + ``'Cont-CC-EESM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Motor: :py:class:`.ExternallyExcitedSynchronousMotor` - Load: :py:class:`.ConstantSpeedLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** + - Ode-Solver: :py:class:`.ScipyOdeSolver` - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` @@ -46,10 +45,10 @@ class AbcContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnv Type: Tuple(State_Space, Reference_Space) State Space: - Box(low=13 * [-1], high=13 * [1]) + Box(low= 15 * [-1], high= 15 * [1]) Reference Space: - Box(low=[-1, -1], high=[1, 1]) + Box(low=[-1, -1, -1], high=[1, 1, 1]) Action Space: Box(low=[-1, -1, -1], high=[1, 1, 1]) @@ -62,7 +61,7 @@ class AbcContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnv >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator >>> >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy-solve_ivp' + >>> my_overridden_solver = 'scipy.solve_ivp' >>> >>> # Update the default arguments to the voltage supply by passing a parameter dict >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} @@ -73,7 +72,7 @@ class AbcContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnv ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-CC-EESM-v0', + ... 'Cont-CC-EESM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -85,9 +84,9 @@ class AbcContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnv >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')), LimitConstraint(('i_e',))), calc_jacobian=True, tau=1e-4): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -95,7 +94,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -143,10 +141,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor=initialize(ps.ElectricMotor, motor, ps.ExternallyExcitedSynchronousMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, - tau=tau, - control_space='abc' + tau=tau ) reference_generator = initialize( ReferenceGenerator, diff --git a/gym_electric_motor/envs/gym_eesm/abccont_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py similarity index 82% rename from gym_electric_motor/envs/gym_eesm/abccont_sc_eesm_env.py rename to gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py index 6114cbce..1f52168e 100644 --- a/gym_electric_motor/envs/gym_eesm/abccont_sc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py @@ -1,29 +1,28 @@ from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem +from gym_electric_motor.physical_systems.physical_systems import ExternallyExcitedSynchronousMotorSystem, SynchronousMotorSystem from gym_electric_motor.visualization import MotorDashboard from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator from gym_electric_motor import physical_systems as ps from gym_electric_motor.reward_functions import WeightedSumOfErrors from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint +from gym_electric_motor.constraints import SquaredConstraint, LimitConstraint -class AbcContSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): +class ContSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): """ Description: - Environment to simulate a abc-domain continuous control set speed controlled permanent magnet synchr. motor. + Environment to simulate a continuous control set speed controlled externally excited synchronous motor. Key: - ``'AbcCont-SC-PMSM-v0'`` + ``'Cont-SC-EESM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Motor: :py:class:`.ExternallyExcitedSynchronousMotor` - Load: :py:class:`.PolynomialStaticLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** + - Ode-Solver: :py:class:`.ScipyOdeSolver` - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` @@ -31,12 +30,12 @@ class AbcContSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir - Visualization: :py:class:`.MotorDashboard` current and action plots - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'``, :py:class:`.LimitConstraint` on the current ``'i_e''`` State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` - Reference Variables: + Referenced Variables: ``['omega']`` Control Cycle Time: @@ -46,13 +45,13 @@ class AbcContSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir Type: Tuple(State_Space, Reference_Space) State Space: - Box(low=13 * [-1], high=13 * [1]) + Box(low=15 * [-1], high=15 * [1]) Reference Space: Box(low=[-1, -1], high=[1, 1]) Action Space: - Box(low=[-1, -1, -1], high=[1, 1, 1]) + Box(low=[-1, -1, -1, -1], high=[1, 1, 1, 1]) Initial State: Zeros on all state variables. @@ -73,7 +72,7 @@ class AbcContSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'AbcCont-SC-EESM-v0', + ... 'Cont-SC-EESM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -85,9 +84,9 @@ class AbcContSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')), LimitConstraint(('i_e',))), calc_jacobian=True, tau=1e-4): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -95,7 +94,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -123,18 +121,25 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve This class is then initialized with its default parameters. The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ - physical_system = SynchronousMotorSystem( + default_subconverters = ( + ps.ContB6BridgeConverter(), + ps.ContFourQuadrantConverter() + ) + physical_system = ExternallyExcitedSynchronousMotorSystem( supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), + converter=initialize( + ps.PowerElectronicConverter, + converter, + ps.ContMultiConverter, + dict(subconverters=default_subconverters) + ), + motor=initialize(ps.ElectricMotor, motor, ps.ExternallyExcitedSynchronousMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict( load_parameter=dict(a=0.01, b=0.01, c=0.0) )), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='abc', ) reference_generator = initialize( ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, dict(reference_state='omega') diff --git a/gym_electric_motor/envs/gym_eesm/dqcont_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py similarity index 83% rename from gym_electric_motor/envs/gym_eesm/dqcont_tc_eesm_env.py rename to gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py index 7bc01a7d..495df721 100644 --- a/gym_electric_motor/envs/gym_eesm/dqcont_tc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py @@ -1,29 +1,29 @@ from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem +from gym_electric_motor.physical_systems.physical_systems import ExternallyExcitedSynchronousMotorSystem, SynchronousMotorSystem from gym_electric_motor.visualization import MotorDashboard from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator from gym_electric_motor import physical_systems as ps from gym_electric_motor.reward_functions import WeightedSumOfErrors from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint +from gym_electric_motor.constraints import LimitConstraint, SquaredConstraint -class DqContTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): +class ContTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): """ Description: - Environment to simulate a dq-domain continuous control set torque controlled permanent magnet synchr. motor. + Environment to simulate a continuous control set torque controlled externally excited synchronous motor. Key: - ``'DqCont-TC-PMSM-v0'`` + ``'Cont-TC-PMSM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Motor: :py:class:`.ExternallyExcitedSynchronousMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` @@ -31,10 +31,10 @@ class DqContTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir - Visualization: :py:class:`.MotorDashboard` torque and action plots - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'``, :py:class:`.LimitConstraint` on the current ``'i_e'`` State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e' 'u_sup']`` Reference Variables: ``['torque']`` @@ -46,13 +46,13 @@ class DqContTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir Type: Tuple(State_Space, Reference_Space) State Space: - Box(low=13 * [-1], high=13 * [1]) + Box(low=15 * [-1], high=15 * [1]) Reference Space: - Box(low=[-1], high=[1]) + Box(low=[-1, -1], high=[1, 1]) Action Space: - Box(low=[-1, -1], high=[1, 1]) + Box(low=[-1, -1, -1, -1], high=[1, 1, 1, 1]) Initial State: Zeros on all state variables. @@ -73,7 +73,7 @@ class DqContTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'DqCont-TC-PMSM-v0', + ... 'Cont-TC-EESM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -85,9 +85,9 @@ class DqContTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): + constraints=(SquaredConstraint(('i_sq', 'i_sd')), LimitConstraint(('i_e',))), calc_jacobian=True, tau=1e-4): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -95,7 +95,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -123,16 +122,23 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve This class is then initialized with its default parameters. The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ - physical_system = SynchronousMotorSystem( + default_subconverters = ( + ps.ContB6BridgeConverter(), + ps.ContFourQuadrantConverter() + ) + physical_system = ExternallyExcitedSynchronousMotorSystem( supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), + converter=initialize( + ps.PowerElectronicConverter, + converter, + ps.ContMultiConverter, + dict(subconverters=default_subconverters) + ), + motor=initialize(ps.ElectricMotor, motor, ps.ExternallyExcitedSynchronousMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau, - control_space='dq' ) reference_generator = initialize( ReferenceGenerator, diff --git a/gym_electric_motor/envs/gym_eesm/dqcont_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/dqcont_cc_eesm_env.py deleted file mode 100644 index 379cdf12..00000000 --- a/gym_electric_motor/envs/gym_eesm/dqcont_cc_eesm_env.py +++ /dev/null @@ -1,160 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain continuous control set current controlled permanent magnet synchr. motor. - - Key: - ``'DqCont-CC-PMSM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.PermanentMagnetSynchronousMotor` - - Load: :py:class:`.ConstantSpeedLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'i_sd' = 0.5, 'i_sq' = 0.5`` - - - Visualization: :py:class:`.MotorDashboard` current and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` - - Reference Variables: - ``['i_sd', 'i_sq']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=13 * [-1], high=13 * [1]) - - Reference Space: - Box(low=[-1, -1], high=[1, 1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='i_sq', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'DqCont-CC-PMSM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - default_subgenerators = ( - WienerProcessReferenceGenerator(reference_state='i_sd'), - WienerProcessReferenceGenerator(reference_state='i_sq') - ) - - physical_system = SynchronousMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq' - ) - reference_generator = initialize( - ReferenceGenerator, - reference_generator, - MultipleReferenceGenerator, - dict(sub_generators=default_subgenerators) - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) - ) - visualization = initialize( - ElectricMotorVisualization, - visualization, - MotorDashboard, - dict(state_plots=('i_sd', 'i_sq'), action_plots='all') - ) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks - ) diff --git a/gym_electric_motor/envs/gym_eesm/dqcont_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/dqcont_sc_eesm_env.py deleted file mode 100644 index 5d1bc71c..00000000 --- a/gym_electric_motor/envs/gym_eesm/dqcont_sc_eesm_env.py +++ /dev/null @@ -1,150 +0,0 @@ -from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ - ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem -from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator -from gym_electric_motor import physical_systems as ps -from gym_electric_motor.reward_functions import WeightedSumOfErrors -from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint - - -class DqContSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): - """ - Description: - Environment to simulate a dq-domain continuous control set speed controlled permanent magnet synchr. motor. - - Key: - ``'DqCont-SC-PMSM-v0'`` - - Default Components: - - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` - - Motor: :py:class:`.PermanentMagnetSynchronousMotor` - - Load: :py:class:`.PolynomialStaticLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** - - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` - - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'omega' = 1.0`` - - - Visualization: :py:class:`.MotorDashboard` current and action plots - - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` - - State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` - - Reference Variables: - ``['omega']`` - - Control Cycle Time: - tau = 1e-4 seconds - - Observation Space: - Type: Tuple(State_Space, Reference_Space) - - State Space: - Box(low=13 * [-1], high=13 * [1]) - - Reference Space: - Box(low=[-1, -1], high=[1, 1]) - - Action Space: - Box(low=[-1, -1], high=[1, 1]) - - Initial State: - Zeros on all state variables. - - Example: - >>> import gym_electric_motor as gem - >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - >>> - >>> # Select a different ode_solver with default parameters by passing a keystring - >>> my_overridden_solver = 'scipy.solve_ivp' - >>> - >>> # Update the default arguments to the voltage supply by passing a parameter dict - >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} - >>> - >>> # Replace the reference generator by passing a new instance - >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='omega', - ... sigma_range=(1e-3, 1e-2) - ... ) - >>> env = gem.make( - ... 'DqCont-SC-PMSM-v0', - ... voltage_supply=my_changed_voltage_supply_args, - ... ode_solver=my_overridden_solver, - ... reference_generator=my_new_ref_gen_instance - ... ) - >>> done = True - >>> for _ in range(1000): - >>> if done: - >>> state, reference = env.reset() - >>> env.render() - >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) - """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, - reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): - """ - Args: - supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment - converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment - motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment - load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment - ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment - reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment - reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment - visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment - constraints(iterable(str/Constraint)): All Constraints of the environment. \n - - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and - passed to the environment. - calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the - simulation. This may lead to speed improvements. Default: True - tau(float): Duration of one control step in seconds. Default: 1e-4. - state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) - callbacks(list(Callback)): Callbacks for user interaction. Default: () - - Note on the env-arg type: - All parameters of type env-arg can be selected as one of the following types: - - **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. - - **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) - - **str:** Pass a string out of the registered classes to select a different class for the component. - This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ - physical_system = SynchronousMotorSystem( - supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict( - load_parameter=dict(a=0.01, b=0.01, c=0.0) - )), - ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), - calc_jacobian=calc_jacobian, - tau=tau, - control_space='dq', - ) - reference_generator = initialize( - ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, dict(reference_state='omega') - ) - reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(omega=1.0)) - ) - visualization = initialize( - ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) - super().__init__( - physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, - constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks - ) diff --git a/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py index 946e1834..be58f247 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py @@ -6,38 +6,37 @@ from gym_electric_motor import physical_systems as ps from gym_electric_motor.reward_functions import WeightedSumOfErrors from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint +from gym_electric_motor.constraints import LimitConstraint, SquaredConstraint class FiniteCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): """ Description: - Environment to simulate a finite control set current controlled permanent magnet synchr. motor. + Environment to simulate a finite control set current controlled externally excited synchronous motor. Key: - ``'Finite-CC-PMSM-v0'`` + ``'Finite-CC-EESM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.FiniteB6BridgeConverter` - - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Converter: :py:class:`.FiniteMultiConverter`(:py:class:`.FiniteB6BridgeConverter`, :py:class:`.FiniteFourQuadrantConverter`) + - Motor: :py:class:`.ExternallyExcitedSynchronousMotor` - Load: :py:class:`.ConstantSpeedLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** + - Ode-Solver: :py:class:`.ScipyOdeSolver` - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq`` + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantities:* ``'i_sd', 'i_sq', 'i_e'`` - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'i_sd' = 0.5, 'i_sq' = 0.5`` + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'i_sd' = 1/3, 'i_sq' = 1/3, 'i_e' = 1/3`` - Visualization: :py:class:`.MotorDashboard` current and action plots - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'``, :py:class:`.LimitConstraint` on the current ``'i_e'`` State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` Reference Variables: - ``['i_sd', 'i_sq']`` + ``['i_sd', 'i_sq', 'i_e']`` Control Cycle Time: tau = 1e-5 seconds @@ -46,13 +45,13 @@ class FiniteCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvi Type: Tuple(State_Space, Reference_Space) State Space: - Box(low=13 * [-1], high=13 * [1]) + Box(low=15 * [-1], high=15 * [1]) Reference Space: - Box(low=[-1, -1], high=[1, 1]) + Box(low=[-1, -1, -1], high=[1, 1, 1]) Action Space: - Discrete(8) + MultiDiscrete((8, 4)) Initial State: Zeros on all state variables. @@ -73,7 +72,7 @@ class FiniteCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvi ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'Finite-CC-PMSM-v0', + ... 'Finite-CC-EESM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -85,9 +84,9 @@ class FiniteCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvi >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')), LimitConstraint(('i_e',))), calc_jacobian=True, tau=1e-5): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -95,7 +94,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -125,16 +123,19 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve """ default_subgenerators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), - WienerProcessReferenceGenerator(reference_state='i_sq') + WienerProcessReferenceGenerator(reference_state='i_sq'), + WienerProcessReferenceGenerator(reference_state='i_e') + ) + default_subconverters = ( + ps.FiniteB6BridgeConverter(), + ps.FiniteFourQuadrantConverter() ) - physical_system = SynchronousMotorSystem( supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.FiniteB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), + converter=initialize(ps.PowerElectronicConverter, converter, ps.FiniteMultiConverter, dict(subconverters=default_subconverters)), + motor=initialize(ps.ElectricMotor, motor, ps.ExternallyExcitedSynchronousMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) @@ -145,13 +146,13 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve dict(sub_generators=default_subgenerators) ) reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=0.5, i_sq=0.5)) + RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(i_sd=1/3, i_sq=1/3, i_e=1/3)) ) visualization = initialize( ElectricMotorVisualization, visualization, MotorDashboard, - dict(state_plots=('i_sd', 'i_sq'), action_plots='all') + dict(state_plots=('i_sd', 'i_sq', 'i_e'), action_plots='all') ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, diff --git a/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py index 46b0c63a..a92203cb 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py @@ -2,42 +2,41 @@ ElectricMotorVisualization from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator +from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator from gym_electric_motor import physical_systems as ps from gym_electric_motor.reward_functions import WeightedSumOfErrors from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint +from gym_electric_motor.constraints import LimitConstraint, SquaredConstraint class FiniteSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): """ Description: - Environment to simulate a finite control set speed controlled permanent magnet synchr. motor. + Environment to simulate a finite control set speed controlled externally excited synchronous motor. Key: - ``'Finite-SC-PMSM-v0'`` + ``'Finite-SC-EESM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.FiniteB6BridgeConverter` - - Motor: :py:class:`.PermanentMagnetSynchronousMotor` - - Load: :py:class:`.PolynomialStaticLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** + - Converter: :py:class:`.FiniteMultiConverter`(:py:class:`.FiniteB6BridgeConverter`, :py:class:`.FiniteFourQuadrantConverter`) + - Motor: :py:class:`.ExternallyExcitedSynchronousMotor` + - Load: :py:class:`.ConstantSpeedLoad` + - Ode-Solver: :py:class:`.ScipyOdeSolver` - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantities:* ``'omega'`` - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'omega' = 1.0`` + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'omega' = 1`` - - Visualization: :py:class:`.MotorDashboard` current and action plots + - Visualization: :py:class:`.MotorDashboard` speed and action plots - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'``, :py:class:`.LimitConstraint` on the current ``'i_e'`` State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` Reference Variables: - ``['omega']`` + ``['i_sd', 'i_sq', 'i_e']`` Control Cycle Time: tau = 1e-5 seconds @@ -46,13 +45,13 @@ class FiniteSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro Type: Tuple(State_Space, Reference_Space) State Space: - Box(low=13 * [-1], high=13 * [1]) + Box(low=15 * [-1], high=15 * [1]) Reference Space: Box(low=[-1], high=[1]) Action Space: - Discrete(8) + MultiDiscrete((8, 4)) Initial State: Zeros on all state variables. @@ -69,11 +68,11 @@ class FiniteSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro >>> >>> # Replace the reference generator by passing a new instance >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='omega', + ... reference_state='i_sq', ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'Finite-SC-PMSM-v0', + ... 'Finite-SC-EESM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -85,9 +84,9 @@ class FiniteSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')), LimitConstraint(('i_e',))), calc_jacobian=True, tau=1e-5): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -95,7 +94,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -123,26 +121,34 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve This class is then initialized with its default parameters. The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ + default_subconverters = ( + ps.FiniteB6BridgeConverter(), + ps.FiniteFourQuadrantConverter() + ) physical_system = SynchronousMotorSystem( supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.FiniteB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict( - load_parameter=dict(a=0.01, b=0.01, c=0.0) - )), + converter=initialize(ps.PowerElectronicConverter, converter, ps.FiniteMultiConverter, dict(subconverters=default_subconverters)), + motor=initialize(ps.ElectricMotor, motor, ps.ExternallyExcitedSynchronousMotor, dict()), + load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) reference_generator = initialize( - ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, dict(reference_state='omega') + ReferenceGenerator, + reference_generator, + WienerProcessReferenceGenerator, + dict(reference_state='omega') ) reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(omega=1.0)) + RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(omega=1)) ) visualization = initialize( - ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('omega',), action_plots='all')) + ElectricMotorVisualization, + visualization, + MotorDashboard, + dict(state_plots=('omega'), action_plots='all') + ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks diff --git a/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py index 113c5350..9b70af18 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py @@ -2,42 +2,41 @@ ElectricMotorVisualization from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator +from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator from gym_electric_motor import physical_systems as ps from gym_electric_motor.reward_functions import WeightedSumOfErrors from gym_electric_motor.utils import initialize -from gym_electric_motor.constraints import SquaredConstraint +from gym_electric_motor.constraints import LimitConstraint, SquaredConstraint class FiniteTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironment): """ Description: - Environment to simulate a finite control set torque controlled permanent magnet synchr. motor. + Environment to simulate a finite control set torque controlled externally excited synchronous motor. Key: - ``'Finite-TC-PMSM-v0'`` + ``'Finite-TC-EESM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.FiniteB6BridgeConverter` - - Motor: :py:class:`.PermanentMagnetSynchronousMotor` + - Converter: :py:class:`.FiniteMultiConverter`(:py:class:`.FiniteB6BridgeConverter`, :py:class:`.FiniteFourQuadrantConverter`) + - Motor: :py:class:`.ExternallyExcitedSynchronousMotor` - Load: :py:class:`.ConstantSpeedLoad` - - Ode-Solver: :py:class:`.EulerSolver` - - Noise: **None** + - Ode-Solver: :py:class:`.ScipyOdeSolver` - - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantities:* ``'torque'`` - - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'torque' = 1.0`` + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'torque' = 1`` - Visualization: :py:class:`.MotorDashboard` torque and action plots - - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'`` + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq'``, :py:class:`.LimitConstraint` on the current ``'i_e'`` State Variables: - ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_sup']`` + ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` Reference Variables: - ``['torque']`` + ``['i_sd', 'i_sq', 'i_e']`` Control Cycle Time: tau = 1e-5 seconds @@ -46,13 +45,13 @@ class FiniteTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir Type: Tuple(State_Space, Reference_Space) State Space: - Box(low=13 * [-1], high=13 * [1]) + Box(low=15 * [-1], high=15 * [1]) Reference Space: - Box(low=[-1], high=[1]) + Box(low=[-1, -1, -1], high=[1, 1, 1]) Action Space: - Discrete(8) + MultiDiscrete((8, 4)) Initial State: Zeros on all state variables. @@ -69,11 +68,11 @@ class FiniteTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir >>> >>> # Replace the reference generator by passing a new instance >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( - ... reference_state='torque', + ... reference_state='i_sq', ... sigma_range=(1e-3, 1e-2) ... ) >>> env = gem.make( - ... 'Finite-TC-PMSM-v0', + ... 'Finite-TC-EESM-v0', ... voltage_supply=my_changed_voltage_supply_args, ... ode_solver=my_overridden_solver, ... reference_generator=my_new_ref_gen_instance @@ -85,9 +84,9 @@ class FiniteTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir >>> env.render() >>> (state, reference), reward, done, _ = env.step(env.action_space.sample()) """ - def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, noise_generator=None, + def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5): + constraints=(SquaredConstraint(('i_sq', 'i_sd')), LimitConstraint(('i_e',))), calc_jacobian=True, tau=1e-5): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -95,7 +94,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment - noise_generator(env-arg): Specification of the :py:class:`.NoiseGenerator` for the environment reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment @@ -122,25 +120,34 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) - """ + """ + default_subconverters = ( + ps.FiniteB6BridgeConverter(), + ps.FiniteFourQuadrantConverter() + ) physical_system = SynchronousMotorSystem( supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.FiniteB6BridgeConverter, dict()), - motor=initialize(ps.ElectricMotor, motor, ps.PermanentMagnetSynchronousMotor, dict()), + converter=initialize(ps.PowerElectronicConverter, converter, ps.FiniteMultiConverter, dict(subconverters=default_subconverters)), + motor=initialize(ps.ElectricMotor, motor, ps.ExternallyExcitedSynchronousMotor, dict()), load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), - noise_generator=initialize(ps.NoiseGenerator, noise_generator, ps.NoiseGenerator, dict()), calc_jacobian=calc_jacobian, tau=tau ) reference_generator = initialize( - ReferenceGenerator, reference_generator, WienerProcessReferenceGenerator, dict(reference_state='torque') + ReferenceGenerator, + reference_generator, + WienerProcessReferenceGenerator, + dict(reference_state='torque') ) reward_function = initialize( - RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(torque=1.0)) + RewardFunction, reward_function, WeightedSumOfErrors, dict(reward_weights=dict(torque=1)) ) visualization = initialize( - ElectricMotorVisualization, visualization, MotorDashboard, dict(state_plots=('torque',), action_plots='all') + ElectricMotorVisualization, + visualization, + MotorDashboard, + dict(state_plots=('torque'), action_plots='all') ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, diff --git a/gym_electric_motor/physical_systems/physical_systems.py b/gym_electric_motor/physical_systems/physical_systems.py index d585e21b..2d1fada6 100644 --- a/gym_electric_motor/physical_systems/physical_systems.py +++ b/gym_electric_motor/physical_systems/physical_systems.py @@ -553,7 +553,7 @@ def reset(self, *_): class ExternallyExcitedSynchronousMotorSystem(SynchronousMotorSystem): """SCML-System that can be used with the externally excited synchronous motor (EESM)""" - def __init__(self, control_space='abc', **kwargs): + def __init__(self, **kwargs): """ Args: control_space(str):('abc' or 'dq') Choose, if actions the actions space is in dq or abc space @@ -561,11 +561,6 @@ def __init__(self, control_space='abc', **kwargs): """ super().__init__(**kwargs) self._action_space = Box(-1,1, shape=(4,), dtype=np.float64) - self.control_space = control_space - if control_space == 'dq': - assert type(self._converter.action_space) == Box, \ - 'dq-control space is only available for Continuous Controlled Converters' - self._action_space = Box(-1, 1, shape=(3,), dtype=np.float64) def _build_state_space(self, state_names): # Docstring of superclass @@ -610,8 +605,6 @@ def simulate(self, action, *_, **__): # Docstring of superclass ode_state = self._ode_solver.y eps = ode_state[self._ode_epsilon_idx] - if self.control_space == 'dq': - action = self.dq_to_abc_space(action, eps) i_in_dq_e = self._electrical_motor.i_in(ode_state[self._ode_currents_idx]) i_in_abc_e = list(self.dq_to_abc_space(i_in_dq_e[:2], eps)) + list(i_in_dq_e[2:]) switching_times = self._converter.set_action(action, self._t) @@ -638,7 +631,6 @@ def simulate(self, action, *_, **__): self._t = self._ode_solver.t self._k += 1 torque = self._electrical_motor.torque(ode_state[self._motor_ode_idx]) - noise = self._noise_generator.noise() mechanical_state = ode_state[self._load_ode_idx] i_dq_e = ode_state[self._ode_currents_idx] i_abc = list( @@ -656,7 +648,7 @@ def simulate(self, action, *_, **__): [eps], u_sup )) - return (system_state + noise) / self._limits + return system_state / self._limits def reset(self, *_): # Docstring of superclass @@ -678,7 +670,6 @@ def reset(self, *_): i_dq = ode_state[self._ode_currents_idx] i_abc = self.dq_to_abc_space(i_dq[:2], eps) torque = self.electrical_motor.torque(motor_state) - noise = self._noise_generator.reset() self._t = 0 self._k = 0 self._ode_solver.set_initial_value(ode_state, self._t) @@ -690,7 +681,7 @@ def reset(self, *_): [eps], u_sup, )) - return (system_state + noise) / self._limits + return system_state / self._limits class SquirrelCageInductionMotorSystem(ThreePhaseMotorSystem): """ From 2e0a08d166ad7ac2bfd2606a1d72540a9fc60bd7 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 14 Feb 2022 20:26:52 +0100 Subject: [PATCH 46/79] EESM documentation --- docs/parts/environments/eesm/cont_cc_eesm.rst | 4 ++ docs/parts/environments/eesm/cont_sc_eesm.rst | 4 ++ docs/parts/environments/eesm/cont_tc_eesm.rst | 4 ++ docs/parts/environments/eesm/eesm_envs.rst | 12 +++++ .../environments/eesm/finite_cc_eesm.rst | 4 ++ .../environments/eesm/finite_sc_eesm.rst | 4 ++ .../environments/eesm/finite_tc_eesm.rst | 4 ++ docs/parts/environments/environment.rst | 52 +++++++++---------- .../{abccont_cc_pmsm.rst => cont_cc_pmsm.rst} | 2 +- .../{abccont_sc_pmsm.rst => cont_sc_pmsm.rst} | 2 +- .../{abccont_tc_pmsm.rst => cont_tc_pmsm.rst} | 2 +- .../environments/pmsm/dqcont_cc_pmsm.rst | 4 -- .../environments/pmsm/dqcont_sc_pmsm.rst | 4 -- .../environments/pmsm/dqcont_tc_pmsm.rst | 4 -- .../physical_systems/electric_motors/eesm.rst | 22 ++++++++ .../electric_motors/electric_motor.rst | 1 + 16 files changed, 88 insertions(+), 41 deletions(-) create mode 100644 docs/parts/environments/eesm/cont_cc_eesm.rst create mode 100644 docs/parts/environments/eesm/cont_sc_eesm.rst create mode 100644 docs/parts/environments/eesm/cont_tc_eesm.rst create mode 100644 docs/parts/environments/eesm/eesm_envs.rst create mode 100644 docs/parts/environments/eesm/finite_cc_eesm.rst create mode 100644 docs/parts/environments/eesm/finite_sc_eesm.rst create mode 100644 docs/parts/environments/eesm/finite_tc_eesm.rst rename docs/parts/environments/pmsm/{abccont_cc_pmsm.rst => cont_cc_pmsm.rst} (70%) rename docs/parts/environments/pmsm/{abccont_sc_pmsm.rst => cont_sc_pmsm.rst} (70%) rename docs/parts/environments/pmsm/{abccont_tc_pmsm.rst => cont_tc_pmsm.rst} (70%) delete mode 100644 docs/parts/environments/pmsm/dqcont_cc_pmsm.rst delete mode 100644 docs/parts/environments/pmsm/dqcont_sc_pmsm.rst delete mode 100644 docs/parts/environments/pmsm/dqcont_tc_pmsm.rst create mode 100644 docs/parts/physical_systems/electric_motors/eesm.rst diff --git a/docs/parts/environments/eesm/cont_cc_eesm.rst b/docs/parts/environments/eesm/cont_cc_eesm.rst new file mode 100644 index 00000000..d382acb0 --- /dev/null +++ b/docs/parts/environments/eesm/cont_cc_eesm.rst @@ -0,0 +1,4 @@ +Continuous Control Set Current Control Externally Excited Synchronous Motor Environment +*************************************************************************************** +.. autoclass:: gym_electric_motor.envs.ContCurrentControlExternallyExcitedSynchronousMotorEnv + :members: diff --git a/docs/parts/environments/eesm/cont_sc_eesm.rst b/docs/parts/environments/eesm/cont_sc_eesm.rst new file mode 100644 index 00000000..48656725 --- /dev/null +++ b/docs/parts/environments/eesm/cont_sc_eesm.rst @@ -0,0 +1,4 @@ +Continuous Control Set Speed Control Externally Excited Synchronous Motor Environment +************************************************************************************** +.. autoclass:: gym_electric_motor.envs.ContSpeedControlExternallyExcitedSynchronousMotorEnv + :members: diff --git a/docs/parts/environments/eesm/cont_tc_eesm.rst b/docs/parts/environments/eesm/cont_tc_eesm.rst new file mode 100644 index 00000000..51200790 --- /dev/null +++ b/docs/parts/environments/eesm/cont_tc_eesm.rst @@ -0,0 +1,4 @@ +Continuous Control Set Torque Control Externally Excited Synchronous Motor Environment +*************************************************************************************** +.. autoclass:: gym_electric_motor.envs.ContTorqueControlExternallyExcitedSynchronousMotorEnv + :members: diff --git a/docs/parts/environments/eesm/eesm_envs.rst b/docs/parts/environments/eesm/eesm_envs.rst new file mode 100644 index 00000000..d8eb6bcc --- /dev/null +++ b/docs/parts/environments/eesm/eesm_envs.rst @@ -0,0 +1,12 @@ +Externally Excited Synchronous Motor Environments +************************************************ + + +.. toctree:: + :maxdepth: 2 + :caption: Environments: + :glob: + + * + + diff --git a/docs/parts/environments/eesm/finite_cc_eesm.rst b/docs/parts/environments/eesm/finite_cc_eesm.rst new file mode 100644 index 00000000..9185fb35 --- /dev/null +++ b/docs/parts/environments/eesm/finite_cc_eesm.rst @@ -0,0 +1,4 @@ +Finite Control Set Current Control Externally Excited Synchronous Motor Environment +********************************************************************************** +.. autoclass:: gym_electric_motor.envs.FiniteCurrentControlExternallyExcitedSynchronousMotorEnv + :members: diff --git a/docs/parts/environments/eesm/finite_sc_eesm.rst b/docs/parts/environments/eesm/finite_sc_eesm.rst new file mode 100644 index 00000000..c66cfd4a --- /dev/null +++ b/docs/parts/environments/eesm/finite_sc_eesm.rst @@ -0,0 +1,4 @@ +Finite Control Set Speed Control Externally Excited Synchronous Motor Environment +******************************************************************************** +.. autoclass:: gym_electric_motor.envs.FiniteSpeedControlPermanentMagnetSynchronousMotorEnv + :members: diff --git a/docs/parts/environments/eesm/finite_tc_eesm.rst b/docs/parts/environments/eesm/finite_tc_eesm.rst new file mode 100644 index 00000000..34626038 --- /dev/null +++ b/docs/parts/environments/eesm/finite_tc_eesm.rst @@ -0,0 +1,4 @@ +Finite Control Set Torque Control Externally Excited Synchronous Motor Environment +********************************************************************************* +.. autoclass:: gym_electric_motor.envs.FiniteTorqueControlExternallyExcitedSynchronousMotorEnv + :members: diff --git a/docs/parts/environments/environment.rst b/docs/parts/environments/environment.rst index 53867de3..9ca95f28 100644 --- a/docs/parts/environments/environment.rst +++ b/docs/parts/environments/environment.rst @@ -6,9 +6,9 @@ In general, all environment-ids are structured as follows: ``ControlType-ControlTask-MotorType-v0`` -- The ``ControlType`` is in ``{Finite / Cont}`` for all DC Motors and in ``{Finite / AbcCont / DqCont}`` for all AC Motors +- The ``ControlType`` is in ``{Finite / Cont}`` for finite control set and continuous control set action spaces - The ``ControlTask`` is in ``{TC / SC / CC}`` (Torque / Speed / Current Control) -- The ``MotorType`` is in ``{PermExDc / ExtExDc / SeriesDc / ShuntDc / PMSM / SynRM / DFIM / SCIM }`` +- The ``MotorType`` is in ``{PermExDc / ExtExDc / SeriesDc / ShuntDc / PMSM / SynRM / / EESM / DFIM / SCIM }`` =================================================================== ============================== @@ -53,50 +53,49 @@ Continuous Current Control Shunt DC Motor Environment ``'Cont-CC- **Permanent Magnet Synchronous Motor (PMSM) Environments** Finite Torque Control PMSM Environment ``'Finite-TC-PMSM-v0'`` -Abc-Continuous Torque Control PMSM Environment ``'AbcCont-TC-PMSM-v0'`` -Dq-Continuous Torque Control PMSM Environment ``'DqCont-TC-PMSM-v0'`` +Torque Control PMSM Environment ``'Cont-TC-PMSM-v0'`` Finite Speed Control PMSM Environment ``'Finite-SC-PMSM-v0'`` -Abc-Continuous Speed Control PMSM Environment ``'Abc-Cont-SC-PMSM-v0'`` -Dq-Continuous Speed Control PMSM Environment ``'Dq-Cont-SC-PMSM-v0'`` +Speed Control PMSM Environment ``'Cont-SC-PMSM-v0'`` Finite Current Control PMSM Environment ``'Finite-CC-PMSM-v0'`` -Abc-Continuous Current Control PMSM Environment ``'AbcCont-CC-PMSM-v0'`` -Dq-Continuous Current Control PMSM Environment ``'DqCont-CC-PMSM-v0'`` +Current Control PMSM Environment ``'Cont-CC-PMSM-v0'`` + + +**Externally Excited Synchronous Motor (EESM) Environments** + +Finite Torque Control EESM Environment ``'Finite-TC-EESM-v0'`` +Torque Control EESM Environment ``'Cont-TC-EESM-v0'`` +Finite Speed Control EESM Environment ``'Finite-SC-EESM-v0'`` +Speed Control EESM Environment ``'Cont-SC-EESM-v0'`` +Finite Current Control EESM Environment ``'Finite-CC-EESM-v0'`` +Current Control EESM Environment ``'Cont-CC-EESM-v0'`` **Synchronous Reluctance Motor (SynRM) Environments** Finite Torque Control SynRM Environment ``'Finite-TC-SynRM-v0'`` -Abc-Continuous Torque Control SynRM Environment ``'AbcCont-TC-SynRM-v0'`` -Dq-Continuous Torque Control SynRM Environment ``'DqCont-TC-SynRM-v0'`` +Torque Control SynRM Environment ``'Cont-TC-SynRM-v0'`` Finite Speed Control SynRM Environment ``'Finite-SC-SynRM-v0'`` -Abc-Continuous Speed Control SynRM Environment ``'Abc-Cont-SC-SynRM-v0'`` -Dq-Continuous Speed Control SynRM Environment ``'Dq-Cont-SC-SynRM-v0'`` +Speed Control SynRM Environment ``'Cont-SC-SynRM-v0'`` Finite Current Control SynRM Environment ``'Finite-CC-SynRM-v0'`` -Abc-Continuous Current Control SynRM Environment ``'AbcCont-CC-SynRM-v0'`` -Dq-Continuous Current Control SynRM Environment ``'DqCont-CC-SynRM-v0'`` +Current Control SynRM Environment ``'Cont-CC-SynRM-v0'`` **Squirrel Cage Induction Motor (SCIM) Environments** Finite Torque Control SCIM Environment ``'Finite-TC-SCIM-v0'`` -Abc-Continuous Torque Control SCIM Environment ``'AbcCont-TC-SCIM-v0'`` -Dq-Continuous Torque Control SCIM Environment ``'DqCont-TC-SCIM-v0'`` +Torque Control SCIM Environment ``'Cont-TC-SCIM-v0'`` Finite Speed Control SCIM Environment ``'Finite-SC-SCIM-v0'`` -Abc-Continuous Speed Control SCIM Environment ``'Abc-Cont-SC-SCIM-v0'`` -Dq-Continuous Speed Control SCIM Environment ``'Dq-Cont-SC-SCIM-v0'`` +Speed Control SCIM Environment ``'Cont-SC-SCIM-v0'`` Finite Current Control SCIM Environment ``'Finite-CC-SCIM-v0'`` -Abc-Continuous Current Control SCIM Environment ``'AbcCont-CC-SCIM-v0'`` -Dq-Continuous Current Control SCIM Environment ``'DqCont-CC-SCIM-v0'`` +Current Control SCIM Environment ``'Cont-CC-SCIM-v0'`` **Doubly Fed Induction Motor (DFIM) Environments** Finite Torque Control DFIM Environment ``'Finite-TC-DFIM-v0'`` -Abc-Continuous Torque Control DFIM Environment ``'AbcCont-TC-DFIM-v0'`` -Dq-Continuous Torque Control DFIM Environment ``'DqCont-TC-DFIM-v0'`` +Torque Control DFIM Environment ``'Cont-TC-DFIM-v0'`` Finite Speed Control DFIM Environment ``'Finite-SC-DFIM-v0'`` -Abc-Continuous Speed Control DFIM Environment ``'Abc-Cont-SC-DFIM-v0'`` -Dq-Continuous Speed Control DFIM Environment ``'Dq-Cont-SC-DFIM-v0'`` +Speed Control DFIM Environment ``'Cont-SC-DFIM-v0'`` Finite Current Control DFIM Environment ``'Finite-CC-DFIM-v0'`` -Abc-Continuous Current Control DFIM Environment ``'AbcCont-CC-DFIM-v0'`` -Dq-Continuous Current Control DFIM Environment ``'DqCont-CC-DFIM-v0'`` +Current Control DFIM Environment ``'Cont-CC-DFIM-v0'`` + =================================================================== ============================== .. toctree:: @@ -109,6 +108,7 @@ Dq-Continuous Current Control DFIM Environment ``'DqCont-C series_dc/series_dc_envs shunt_dc/shunt_dc_envs pmsm/pmsm_envs + eesm/eesm_envs synrm/synrm_envs scim/scim_envs dfim/dfim_envs diff --git a/docs/parts/environments/pmsm/abccont_cc_pmsm.rst b/docs/parts/environments/pmsm/cont_cc_pmsm.rst similarity index 70% rename from docs/parts/environments/pmsm/abccont_cc_pmsm.rst rename to docs/parts/environments/pmsm/cont_cc_pmsm.rst index bb33a15c..c821ed8c 100644 --- a/docs/parts/environments/pmsm/abccont_cc_pmsm.rst +++ b/docs/parts/environments/pmsm/cont_cc_pmsm.rst @@ -1,4 +1,4 @@ -Abc-Continuous Current Control Permanent Magnet Synchronous Motor Environment +Current Control Permanent Magnet Synchronous Motor Environment ***************************************************************************** .. autoclass:: gym_electric_motor.envs.ContCurrentControlPermanentMagnetSynchronousMotorEnv :members: diff --git a/docs/parts/environments/pmsm/abccont_sc_pmsm.rst b/docs/parts/environments/pmsm/cont_sc_pmsm.rst similarity index 70% rename from docs/parts/environments/pmsm/abccont_sc_pmsm.rst rename to docs/parts/environments/pmsm/cont_sc_pmsm.rst index fc16512d..cf3e5122 100644 --- a/docs/parts/environments/pmsm/abccont_sc_pmsm.rst +++ b/docs/parts/environments/pmsm/cont_sc_pmsm.rst @@ -1,4 +1,4 @@ -Abc-Continuous Speed Control Permanent Magnet Synchronous Motor Environment +Speed Control Permanent Magnet Synchronous Motor Environment **************************************************************************** .. autoclass:: gym_electric_motor.envs.ContSpeedControlPermanentMagnetSynchronousMotorEnv :members: diff --git a/docs/parts/environments/pmsm/abccont_tc_pmsm.rst b/docs/parts/environments/pmsm/cont_tc_pmsm.rst similarity index 70% rename from docs/parts/environments/pmsm/abccont_tc_pmsm.rst rename to docs/parts/environments/pmsm/cont_tc_pmsm.rst index 2b771494..d661e28c 100644 --- a/docs/parts/environments/pmsm/abccont_tc_pmsm.rst +++ b/docs/parts/environments/pmsm/cont_tc_pmsm.rst @@ -1,4 +1,4 @@ -Abc-Continuous Torque Control Permanent Magnet Synchronous Motor Environment +Torque Control Permanent Magnet Synchronous Motor Environment ****************************************************************************** .. autoclass:: gym_electric_motor.envs.ContTorqueControlPermanentMagnetSynchronousMotorEnv :members: diff --git a/docs/parts/environments/pmsm/dqcont_cc_pmsm.rst b/docs/parts/environments/pmsm/dqcont_cc_pmsm.rst deleted file mode 100644 index ee2022c4..00000000 --- a/docs/parts/environments/pmsm/dqcont_cc_pmsm.rst +++ /dev/null @@ -1,4 +0,0 @@ -Dq-Continuous Current Control Permanent Magnet Synchronous Motor Environment -***************************************************************************** -.. autoclass:: gym_electric_motor.envs.DqContCurrentControlPermanentMagnetSynchronousMotorEnv - :members: diff --git a/docs/parts/environments/pmsm/dqcont_sc_pmsm.rst b/docs/parts/environments/pmsm/dqcont_sc_pmsm.rst deleted file mode 100644 index aa11038f..00000000 --- a/docs/parts/environments/pmsm/dqcont_sc_pmsm.rst +++ /dev/null @@ -1,4 +0,0 @@ -Dq-Continuous Speed Control Permanent Magnet Synchronous Motor Environment -**************************************************************************** -.. autoclass:: gym_electric_motor.envs.DqContSpeedControlPermanentMagnetSynchronousMotorEnv - :members: diff --git a/docs/parts/environments/pmsm/dqcont_tc_pmsm.rst b/docs/parts/environments/pmsm/dqcont_tc_pmsm.rst deleted file mode 100644 index 9a962707..00000000 --- a/docs/parts/environments/pmsm/dqcont_tc_pmsm.rst +++ /dev/null @@ -1,4 +0,0 @@ -Dq-Continuous Torque Control Permanent Magnet Synchronous Motor Environment -**************************************************************************** -.. autoclass:: gym_electric_motor.envs.DqContTorqueControlPermanentMagnetSynchronousMotorEnv - :members: diff --git a/docs/parts/physical_systems/electric_motors/eesm.rst b/docs/parts/physical_systems/electric_motors/eesm.rst new file mode 100644 index 00000000..700f63f6 --- /dev/null +++ b/docs/parts/physical_systems/electric_motors/eesm.rst @@ -0,0 +1,22 @@ +Extertnally Excited Synchronous Motor +###################################### + +Schematic +********* + + +Electrical ODE +************** + + + +Torque Equation +*************** + + +Code Documentation +****************** + +.. autoclass:: gym_electric_motor.physical_systems.electric_motors.PermanentMagnetSynchronousMotor + :members: + :inherited-members: diff --git a/docs/parts/physical_systems/electric_motors/electric_motor.rst b/docs/parts/physical_systems/electric_motors/electric_motor.rst index abeaf663..18b0902f 100644 --- a/docs/parts/physical_systems/electric_motors/electric_motor.rst +++ b/docs/parts/physical_systems/electric_motors/electric_motor.rst @@ -10,6 +10,7 @@ Electric Motors series shunt pmsm + eesm synrm scim dfim From a01ff97a89eaf7a445e6a5686e79d94543dafcbe Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 14 Feb 2022 20:43:47 +0100 Subject: [PATCH 47/79] bugfixes --- .../envs/gym_eesm/finite_cc_eesm_env.py | 4 ++-- .../envs/gym_eesm/finite_sc_eesm_env.py | 12 ++++++------ .../envs/gym_eesm/finite_tc_eesm_env.py | 8 ++++---- .../integration_tests/test_environment_execution.py | 2 +- tests/integration_tests/test_environment_seeding.py | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py index be58f247..fe4ba82e 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py @@ -1,6 +1,6 @@ from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem +from gym_electric_motor.physical_systems.physical_systems import ExternallyExcitedSynchronousMotorSystem from gym_electric_motor.visualization import MotorDashboard from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator from gym_electric_motor import physical_systems as ps @@ -130,7 +130,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ps.FiniteB6BridgeConverter(), ps.FiniteFourQuadrantConverter() ) - physical_system = SynchronousMotorSystem( + physical_system = ExternallyExcitedSynchronousMotorSystem( supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), converter=initialize(ps.PowerElectronicConverter, converter, ps.FiniteMultiConverter, dict(subconverters=default_subconverters)), motor=initialize(ps.ElectricMotor, motor, ps.ExternallyExcitedSynchronousMotor, dict()), diff --git a/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py index a92203cb..267fd2a4 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py @@ -1,8 +1,8 @@ from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem +from gym_electric_motor.physical_systems.physical_systems import ExternallyExcitedSynchronousMotorSystem from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator +from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator from gym_electric_motor import physical_systems as ps from gym_electric_motor.reward_functions import WeightedSumOfErrors from gym_electric_motor.utils import initialize @@ -21,7 +21,7 @@ class FiniteSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro - Supply: :py:class:`.IdealVoltageSupply` - Converter: :py:class:`.FiniteMultiConverter`(:py:class:`.FiniteB6BridgeConverter`, :py:class:`.FiniteFourQuadrantConverter`) - Motor: :py:class:`.ExternallyExcitedSynchronousMotor` - - Load: :py:class:`.ConstantSpeedLoad` + - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.ScipyOdeSolver` - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantities:* ``'omega'`` @@ -125,11 +125,11 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ps.FiniteB6BridgeConverter(), ps.FiniteFourQuadrantConverter() ) - physical_system = SynchronousMotorSystem( + physical_system = ExternallyExcitedSynchronousMotorSystem( supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), converter=initialize(ps.PowerElectronicConverter, converter, ps.FiniteMultiConverter, dict(subconverters=default_subconverters)), motor=initialize(ps.ElectricMotor, motor, ps.ExternallyExcitedSynchronousMotor, dict()), - load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict), + load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict()), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), calc_jacobian=calc_jacobian, tau=tau @@ -147,7 +147,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, - dict(state_plots=('omega'), action_plots='all') + dict(state_plots=('omega',), action_plots='all') ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, diff --git a/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py index 9b70af18..11062e99 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py @@ -1,8 +1,8 @@ from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ ElectricMotorVisualization -from gym_electric_motor.physical_systems.physical_systems import SynchronousMotorSystem +from gym_electric_motor.physical_systems.physical_systems import ExternallyExcitedSynchronousMotorSystem from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator, MultipleReferenceGenerator +from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator from gym_electric_motor import physical_systems as ps from gym_electric_motor.reward_functions import WeightedSumOfErrors from gym_electric_motor.utils import initialize @@ -125,7 +125,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ps.FiniteB6BridgeConverter(), ps.FiniteFourQuadrantConverter() ) - physical_system = SynchronousMotorSystem( + physical_system = ExternallyExcitedSynchronousMotorSystem( supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=420.0)), converter=initialize(ps.PowerElectronicConverter, converter, ps.FiniteMultiConverter, dict(subconverters=default_subconverters)), motor=initialize(ps.ElectricMotor, motor, ps.ExternallyExcitedSynchronousMotor, dict()), @@ -147,7 +147,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ElectricMotorVisualization, visualization, MotorDashboard, - dict(state_plots=('torque'), action_plots='all') + dict(state_plots=('torque',), action_plots='all') ) super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, diff --git a/tests/integration_tests/test_environment_execution.py b/tests/integration_tests/test_environment_execution.py index c3e4d703..2815f905 100644 --- a/tests/integration_tests/test_environment_execution.py +++ b/tests/integration_tests/test_environment_execution.py @@ -4,7 +4,7 @@ control_tasks = ['TC', 'SC', 'CC'] action_types = ['Cont', 'Finite'] -motors = ['SeriesDc', 'PermExDc', 'ExtExDc', 'ShuntDc', 'PMSM', 'SynRM', 'DFIM', 'SCIM'] +motors = ['SeriesDc', 'PermExDc', 'ExtExDc', 'ShuntDc', 'PMSM', 'EESM', 'SynRM', 'DFIM', 'SCIM'] versions = ['v0'] diff --git a/tests/integration_tests/test_environment_seeding.py b/tests/integration_tests/test_environment_seeding.py index 4953d0eb..db5c6cfb 100644 --- a/tests/integration_tests/test_environment_seeding.py +++ b/tests/integration_tests/test_environment_seeding.py @@ -4,7 +4,7 @@ control_tasks = ['TC', 'SC', 'CC'] action_types = ['Cont', 'Finite'] -motors = ['SeriesDc', 'PermExDc', 'ExtExDc', 'ShuntDc', 'PMSM', 'SynRM', 'DFIM', 'SCIM'] +motors = ['SeriesDc', 'PermExDc', 'ExtExDc', 'ShuntDc', 'PMSM', 'EESM', 'SynRM', 'DFIM', 'SCIM'] versions = ['v0'] seeds = [123, 456, 789] From ef3f93176390e8415b7c8b11c87081370d3132f6 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 14 Feb 2022 20:47:43 +0100 Subject: [PATCH 48/79] bugfix --- gym_electric_motor/physical_systems/physical_systems.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/gym_electric_motor/physical_systems/physical_systems.py b/gym_electric_motor/physical_systems/physical_systems.py index 2d1fada6..ea37dd54 100644 --- a/gym_electric_motor/physical_systems/physical_systems.py +++ b/gym_electric_motor/physical_systems/physical_systems.py @@ -553,14 +553,6 @@ def reset(self, *_): class ExternallyExcitedSynchronousMotorSystem(SynchronousMotorSystem): """SCML-System that can be used with the externally excited synchronous motor (EESM)""" - def __init__(self, **kwargs): - """ - Args: - control_space(str):('abc' or 'dq') Choose, if actions the actions space is in dq or abc space - kwargs: Further arguments to pass tp SCMLSystem - """ - super().__init__(**kwargs) - self._action_space = Box(-1,1, shape=(4,), dtype=np.float64) def _build_state_space(self, state_names): # Docstring of superclass From d75eadc3b05045b9a9508acbecace1186643de46 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 21 Feb 2022 20:07:37 +0100 Subject: [PATCH 49/79] Bugfixes --- .../ddpg_pmsm_dq_current_control.py | 10 +++++----- .../state_action_processors/dead_time_processor.py | 1 + .../state_action_processors/state_action_processor.py | 4 ++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py b/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py index 1b9679b0..23a8aec4 100644 --- a/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py +++ b/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py @@ -20,6 +20,7 @@ from gym.core import Wrapper from gym.spaces import Box, Tuple from gym_electric_motor.constraints import SquaredConstraint +from gym_electric_motor.state_action_processors import DqToAbcActionProcessor, DeadTimeProcessor ''' This example shows how we can use GEM to train a reinforcement learning agent to control the current within @@ -96,12 +97,13 @@ def reset(self, **kwargs): # Change the motor nominal values nominal_values = {key: 0.7 * limit for key, limit in limit_values.items()} - + state_action_processors = (DeadTimeProcessor(), DqToAbcActionProcessor.make('PMSM')) # Create the environment env = gem.make( # Choose the permanent magnet synchronous motor with continuous-control-set - 'DqCont-CC-PMSM-v0', + 'Cont-CC-PMSM-v0', # Pass a class with extra parameters + state_action_processors=state_action_processors, visualization=MotorDashboard( state_plots=['i_sq', 'i_sd'], action_plots='all', @@ -132,9 +134,7 @@ def reset(self, **kwargs): # Consider converter dead time within the simulation # This means that a given action will show effect only with one step delay # This is realistic behavior of drive applications - converter=dict( - dead_time=True, - ), + # Set the DC-link supply voltage supply=dict( u_nominal=400 diff --git a/gym_electric_motor/state_action_processors/dead_time_processor.py b/gym_electric_motor/state_action_processors/dead_time_processor.py index a9d3feca..8acf0d6a 100644 --- a/gym_electric_motor/state_action_processors/dead_time_processor.py +++ b/gym_electric_motor/state_action_processors/dead_time_processor.py @@ -57,6 +57,7 @@ def set_physical_system(self, physical_system): 'Only Discrete / MultiDiscrete and Box allowed for the dead time processor.' ) self._reset_actions = lambda: [reset_action] * self._action_deque.maxlen + return self def reset(self): """Resets the processor and the inner physical system for a new episode. diff --git a/gym_electric_motor/state_action_processors/state_action_processor.py b/gym_electric_motor/state_action_processors/state_action_processor.py index 8d44d524..41719871 100644 --- a/gym_electric_motor/state_action_processors/state_action_processor.py +++ b/gym_electric_motor/state_action_processors/state_action_processor.py @@ -9,6 +9,10 @@ class StateActionProcessor(gem.core.PhysicalSystem, gem.core.RandomComponent): other components of the environment. """ + @property + def k(self): + return self._physical_system.k + @property def action_space(self): """The processed action space. From 1ec2a37e25493f6bc128e2ffef95713c9bad7076 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 21 Feb 2022 20:43:17 +0100 Subject: [PATCH 50/79] cookbook custom processor --- .../environment_features/GEM_cookbook.ipynb | 2104 ++--------------- 1 file changed, 145 insertions(+), 1959 deletions(-) diff --git a/examples/environment_features/GEM_cookbook.ipynb b/examples/environment_features/GEM_cookbook.ipynb index 5b6d5c13..23dee129 100644 --- a/examples/environment_features/GEM_cookbook.ipynb +++ b/examples/environment_features/GEM_cookbook.ipynb @@ -174,976 +174,24 @@ "outputs": [ { "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "/* global mpl */\n", - "window.mpl = {};\n", - "\n", - "mpl.get_websocket_type = function () {\n", - " if (typeof WebSocket !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof MozWebSocket !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert(\n", - " 'Your browser does not have WebSocket support. ' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.'\n", - " );\n", - " }\n", - "};\n", - "\n", - "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = this.ws.binaryType !== undefined;\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById('mpl-warnings');\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent =\n", - " 'This browser does not support binary websocket messages. ' +\n", - " 'Performance may be slow.';\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = document.createElement('div');\n", - " this.root.setAttribute('style', 'display: inline-block');\n", - " this._root_extra_style(this.root);\n", - "\n", - " parent_element.appendChild(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message('supports_binary', { value: fig.supports_binary });\n", - " fig.send_message('send_image_mode', {});\n", - " if (fig.ratio !== 1) {\n", - " fig.send_message('set_device_pixel_ratio', {\n", - " device_pixel_ratio: fig.ratio,\n", - " });\n", - " }\n", - " fig.send_message('refresh', {});\n", - " };\n", - "\n", - " this.imageObj.onload = function () {\n", - " if (fig.image_mode === 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function () {\n", - " fig.ws.close();\n", - " };\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "};\n", - "\n", - "mpl.figure.prototype._init_header = function () {\n", - " var titlebar = document.createElement('div');\n", - " titlebar.classList =\n", - " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", - " var titletext = document.createElement('div');\n", - " titletext.classList = 'ui-dialog-title';\n", - " titletext.setAttribute(\n", - " 'style',\n", - " 'width: 100%; text-align: center; padding: 3px;'\n", - " );\n", - " titlebar.appendChild(titletext);\n", - " this.root.appendChild(titlebar);\n", - " this.header = titletext;\n", - "};\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", - "\n", - "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", - "\n", - "mpl.figure.prototype._init_canvas = function () {\n", - " var fig = this;\n", - "\n", - " var canvas_div = (this.canvas_div = document.createElement('div'));\n", - " canvas_div.setAttribute(\n", - " 'style',\n", - " 'border: 1px solid #ddd;' +\n", - " 'box-sizing: content-box;' +\n", - " 'clear: both;' +\n", - " 'min-height: 1px;' +\n", - " 'min-width: 1px;' +\n", - " 'outline: 0;' +\n", - " 'overflow: hidden;' +\n", - " 'position: relative;' +\n", - " 'resize: both;'\n", - " );\n", - "\n", - " function on_keyboard_event_closure(name) {\n", - " return function (event) {\n", - " return fig.key_event(event, name);\n", - " };\n", - " }\n", - "\n", - " canvas_div.addEventListener(\n", - " 'keydown',\n", - " on_keyboard_event_closure('key_press')\n", - " );\n", - " canvas_div.addEventListener(\n", - " 'keyup',\n", - " on_keyboard_event_closure('key_release')\n", - " );\n", - "\n", - " this._canvas_extra_style(canvas_div);\n", - " this.root.appendChild(canvas_div);\n", - "\n", - " var canvas = (this.canvas = document.createElement('canvas'));\n", - " canvas.classList.add('mpl-canvas');\n", - " canvas.setAttribute('style', 'box-sizing: content-box;');\n", - "\n", - " this.context = canvas.getContext('2d');\n", - "\n", - " var backingStore =\n", - " this.context.backingStorePixelRatio ||\n", - " this.context.webkitBackingStorePixelRatio ||\n", - " this.context.mozBackingStorePixelRatio ||\n", - " this.context.msBackingStorePixelRatio ||\n", - " this.context.oBackingStorePixelRatio ||\n", - " this.context.backingStorePixelRatio ||\n", - " 1;\n", - "\n", - " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", - " 'canvas'\n", - " ));\n", - " rubberband_canvas.setAttribute(\n", - " 'style',\n", - " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", - " );\n", - "\n", - " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", - " if (this.ResizeObserver === undefined) {\n", - " if (window.ResizeObserver !== undefined) {\n", - " this.ResizeObserver = window.ResizeObserver;\n", - " } else {\n", - " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", - " this.ResizeObserver = obs.ResizeObserver;\n", - " }\n", - " }\n", - "\n", - " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", - " var nentries = entries.length;\n", - " for (var i = 0; i < nentries; i++) {\n", - " var entry = entries[i];\n", - " var width, height;\n", - " if (entry.contentBoxSize) {\n", - " if (entry.contentBoxSize instanceof Array) {\n", - " // Chrome 84 implements new version of spec.\n", - " width = entry.contentBoxSize[0].inlineSize;\n", - " height = entry.contentBoxSize[0].blockSize;\n", - " } else {\n", - " // Firefox implements old version of spec.\n", - " width = entry.contentBoxSize.inlineSize;\n", - " height = entry.contentBoxSize.blockSize;\n", - " }\n", - " } else {\n", - " // Chrome <84 implements even older version of spec.\n", - " width = entry.contentRect.width;\n", - " height = entry.contentRect.height;\n", - " }\n", - "\n", - " // Keep the size of the canvas and rubber band canvas in sync with\n", - " // the canvas container.\n", - " if (entry.devicePixelContentBoxSize) {\n", - " // Chrome 84 implements new version of spec.\n", - " canvas.setAttribute(\n", - " 'width',\n", - " entry.devicePixelContentBoxSize[0].inlineSize\n", - " );\n", - " canvas.setAttribute(\n", - " 'height',\n", - " entry.devicePixelContentBoxSize[0].blockSize\n", - " );\n", - " } else {\n", - " canvas.setAttribute('width', width * fig.ratio);\n", - " canvas.setAttribute('height', height * fig.ratio);\n", - " }\n", - " canvas.setAttribute(\n", - " 'style',\n", - " 'width: ' + width + 'px; height: ' + height + 'px;'\n", - " );\n", - "\n", - " rubberband_canvas.setAttribute('width', width);\n", - " rubberband_canvas.setAttribute('height', height);\n", - "\n", - " // And update the size in Python. We ignore the initial 0/0 size\n", - " // that occurs as the element is placed into the DOM, which should\n", - " // otherwise not happen due to the minimum size styling.\n", - " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", - " fig.request_resize(width, height);\n", - " }\n", - " }\n", - " });\n", - " this.resizeObserverInstance.observe(canvas_div);\n", - "\n", - " function on_mouse_event_closure(name) {\n", - " return function (event) {\n", - " return fig.mouse_event(event, name);\n", - " };\n", - " }\n", - "\n", - " rubberband_canvas.addEventListener(\n", - " 'mousedown',\n", - " on_mouse_event_closure('button_press')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseup',\n", - " on_mouse_event_closure('button_release')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'dblclick',\n", - " on_mouse_event_closure('dblclick')\n", - " );\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband_canvas.addEventListener(\n", - " 'mousemove',\n", - " on_mouse_event_closure('motion_notify')\n", - " );\n", - "\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseenter',\n", - " on_mouse_event_closure('figure_enter')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseleave',\n", - " on_mouse_event_closure('figure_leave')\n", - " );\n", - "\n", - " canvas_div.addEventListener('wheel', function (event) {\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " on_mouse_event_closure('scroll')(event);\n", - " });\n", - "\n", - " canvas_div.appendChild(canvas);\n", - " canvas_div.appendChild(rubberband_canvas);\n", - "\n", - " this.rubberband_context = rubberband_canvas.getContext('2d');\n", - " this.rubberband_context.strokeStyle = '#000000';\n", - "\n", - " this._resize_canvas = function (width, height, forward) {\n", - " if (forward) {\n", - " canvas_div.style.width = width + 'px';\n", - " canvas_div.style.height = height + 'px';\n", - " }\n", - " };\n", - "\n", - " // Disable right mouse context menu.\n", - " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", - " event.preventDefault();\n", - " return false;\n", - " });\n", - "\n", - " function set_focus() {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "};\n", - "\n", - "mpl.figure.prototype._init_toolbar = function () {\n", - " var fig = this;\n", - "\n", - " var toolbar = document.createElement('div');\n", - " toolbar.classList = 'mpl-toolbar';\n", - " this.root.appendChild(toolbar);\n", - "\n", - " function on_click_closure(name) {\n", - " return function (_event) {\n", - " return fig.toolbar_button_onclick(name);\n", - " };\n", - " }\n", - "\n", - " function on_mouseover_closure(tooltip) {\n", - " return function (event) {\n", - " if (!event.currentTarget.disabled) {\n", - " return fig.toolbar_button_onmouseover(tooltip);\n", - " }\n", - " };\n", - " }\n", - "\n", - " fig.buttons = {};\n", - " var buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'mpl-button-group';\n", - " for (var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " /* Instead of a spacer, we start a new button group. */\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - " buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'mpl-button-group';\n", - " continue;\n", - " }\n", - "\n", - " var button = (fig.buttons[name] = document.createElement('button'));\n", - " button.classList = 'mpl-widget';\n", - " button.setAttribute('role', 'button');\n", - " button.setAttribute('aria-disabled', 'false');\n", - " button.addEventListener('click', on_click_closure(method_name));\n", - " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", - "\n", - " var icon_img = document.createElement('img');\n", - " icon_img.src = '_images/' + image + '.png';\n", - " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", - " icon_img.alt = tooltip;\n", - " button.appendChild(icon_img);\n", - "\n", - " buttonGroup.appendChild(button);\n", - " }\n", - "\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - "\n", - " var fmt_picker = document.createElement('select');\n", - " fmt_picker.classList = 'mpl-widget';\n", - " toolbar.appendChild(fmt_picker);\n", - " this.format_dropdown = fmt_picker;\n", - "\n", - " for (var ind in mpl.extensions) {\n", - " var fmt = mpl.extensions[ind];\n", - " var option = document.createElement('option');\n", - " option.selected = fmt === mpl.default_extension;\n", - " option.innerHTML = fmt;\n", - " fmt_picker.appendChild(option);\n", - " }\n", - "\n", - " var status_bar = document.createElement('span');\n", - " status_bar.classList = 'mpl-message';\n", - " toolbar.appendChild(status_bar);\n", - " this.message = status_bar;\n", - "};\n", - "\n", - "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", - " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", - " // which will in turn request a refresh of the image.\n", - " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", - "};\n", - "\n", - "mpl.figure.prototype.send_message = function (type, properties) {\n", - " properties['type'] = type;\n", - " properties['figure_id'] = this.id;\n", - " this.ws.send(JSON.stringify(properties));\n", - "};\n", - "\n", - "mpl.figure.prototype.send_draw_message = function () {\n", - " if (!this.waiting) {\n", - " this.waiting = true;\n", - " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", - " var format_dropdown = fig.format_dropdown;\n", - " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", - " fig.ondownload(fig, format);\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", - " var size = msg['size'];\n", - " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", - " fig._resize_canvas(size[0], size[1], msg['forward']);\n", - " fig.send_message('refresh', {});\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", - " var x0 = msg['x0'] / fig.ratio;\n", - " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", - " var x1 = msg['x1'] / fig.ratio;\n", - " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", - " x0 = Math.floor(x0) + 0.5;\n", - " y0 = Math.floor(y0) + 0.5;\n", - " x1 = Math.floor(x1) + 0.5;\n", - " y1 = Math.floor(y1) + 0.5;\n", - " var min_x = Math.min(x0, x1);\n", - " var min_y = Math.min(y0, y1);\n", - " var width = Math.abs(x1 - x0);\n", - " var height = Math.abs(y1 - y0);\n", - "\n", - " fig.rubberband_context.clearRect(\n", - " 0,\n", - " 0,\n", - " fig.canvas.width / fig.ratio,\n", - " fig.canvas.height / fig.ratio\n", - " );\n", - "\n", - " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", - " // Updates the figure title.\n", - " fig.header.textContent = msg['label'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", - " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_message = function (fig, msg) {\n", - " fig.message.textContent = msg['message'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", - " // Request the server to send over a new figure.\n", - " fig.send_draw_message();\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", - " fig.image_mode = msg['mode'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", - " for (var key in msg) {\n", - " if (!(key in fig.buttons)) {\n", - " continue;\n", - " }\n", - " fig.buttons[key].disabled = !msg[key];\n", - " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", - " if (msg['mode'] === 'PAN') {\n", - " fig.buttons['Pan'].classList.add('active');\n", - " fig.buttons['Zoom'].classList.remove('active');\n", - " } else if (msg['mode'] === 'ZOOM') {\n", - " fig.buttons['Pan'].classList.remove('active');\n", - " fig.buttons['Zoom'].classList.add('active');\n", - " } else {\n", - " fig.buttons['Pan'].classList.remove('active');\n", - " fig.buttons['Zoom'].classList.remove('active');\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.updated_canvas_event = function () {\n", - " // Called whenever the canvas gets updated.\n", - " this.send_message('ack', {});\n", - "};\n", - "\n", - "// A function to construct a web socket function for onmessage handling.\n", - "// Called in the figure constructor.\n", - "mpl.figure.prototype._make_on_message_function = function (fig) {\n", - " return function socket_on_message(evt) {\n", - " if (evt.data instanceof Blob) {\n", - " var img = evt.data;\n", - " if (img.type !== 'image/png') {\n", - " /* FIXME: We get \"Resource interpreted as Image but\n", - " * transferred with MIME type text/plain:\" errors on\n", - " * Chrome. But how to set the MIME type? It doesn't seem\n", - " * to be part of the websocket stream */\n", - " img.type = 'image/png';\n", - " }\n", - "\n", - " /* Free the memory for the previous frames */\n", - " if (fig.imageObj.src) {\n", - " (window.URL || window.webkitURL).revokeObjectURL(\n", - " fig.imageObj.src\n", - " );\n", - " }\n", - "\n", - " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", - " img\n", - " );\n", - " fig.updated_canvas_event();\n", - " fig.waiting = false;\n", - " return;\n", - " } else if (\n", - " typeof evt.data === 'string' &&\n", - " evt.data.slice(0, 21) === 'data:image/png;base64'\n", - " ) {\n", - " fig.imageObj.src = evt.data;\n", - " fig.updated_canvas_event();\n", - " fig.waiting = false;\n", - " return;\n", - " }\n", - "\n", - " var msg = JSON.parse(evt.data);\n", - " var msg_type = msg['type'];\n", - "\n", - " // Call the \"handle_{type}\" callback, which takes\n", - " // the figure and JSON message as its only arguments.\n", - " try {\n", - " var callback = fig['handle_' + msg_type];\n", - " } catch (e) {\n", - " console.log(\n", - " \"No handler for the '\" + msg_type + \"' message type: \",\n", - " msg\n", - " );\n", - " return;\n", - " }\n", - "\n", - " if (callback) {\n", - " try {\n", - " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", - " callback(fig, msg);\n", - " } catch (e) {\n", - " console.log(\n", - " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", - " e,\n", - " e.stack,\n", - " msg\n", - " );\n", - " }\n", - " }\n", - " };\n", - "};\n", - "\n", - "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", - "mpl.findpos = function (e) {\n", - " //this section is from http://www.quirksmode.org/js/events_properties.html\n", - " var targ;\n", - " if (!e) {\n", - " e = window.event;\n", - " }\n", - " if (e.target) {\n", - " targ = e.target;\n", - " } else if (e.srcElement) {\n", - " targ = e.srcElement;\n", - " }\n", - " if (targ.nodeType === 3) {\n", - " // defeat Safari bug\n", - " targ = targ.parentNode;\n", - " }\n", - "\n", - " // pageX,Y are the mouse positions relative to the document\n", - " var boundingRect = targ.getBoundingClientRect();\n", - " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", - " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", - "\n", - " return { x: x, y: y };\n", - "};\n", - "\n", - "/*\n", - " * return a copy of an object with only non-object keys\n", - " * we need this to avoid circular references\n", - " * https://stackoverflow.com/a/24161582/3208463\n", - " */\n", - "function simpleKeys(original) {\n", - " return Object.keys(original).reduce(function (obj, key) {\n", - " if (typeof original[key] !== 'object') {\n", - " obj[key] = original[key];\n", - " }\n", - " return obj;\n", - " }, {});\n", - "}\n", - "\n", - "mpl.figure.prototype.mouse_event = function (event, name) {\n", - " var canvas_pos = mpl.findpos(event);\n", - "\n", - " if (name === 'button_press') {\n", - " this.canvas.focus();\n", - " this.canvas_div.focus();\n", - " }\n", - "\n", - " var x = canvas_pos.x * this.ratio;\n", - " var y = canvas_pos.y * this.ratio;\n", - "\n", - " this.send_message(name, {\n", - " x: x,\n", - " y: y,\n", - " button: event.button,\n", - " step: event.step,\n", - " guiEvent: simpleKeys(event),\n", - " });\n", - "\n", - " /* This prevents the web browser from automatically changing to\n", - " * the text insertion cursor when the button is pressed. We want\n", - " * to control all of the cursor setting manually through the\n", - " * 'cursor' event from matplotlib */\n", - " event.preventDefault();\n", - " return false;\n", - "};\n", - "\n", - "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", - " // Handle any extra behaviour associated with a key event\n", - "};\n", - "\n", - "mpl.figure.prototype.key_event = function (event, name) {\n", - " // Prevent repeat events\n", - " if (name === 'key_press') {\n", - " if (event.key === this._key) {\n", - " return;\n", - " } else {\n", - " this._key = event.key;\n", - " }\n", - " }\n", - " if (name === 'key_release') {\n", - " this._key = null;\n", - " }\n", - "\n", - " var value = '';\n", - " if (event.ctrlKey && event.key !== 'Control') {\n", - " value += 'ctrl+';\n", - " }\n", - " else if (event.altKey && event.key !== 'Alt') {\n", - " value += 'alt+';\n", - " }\n", - " else if (event.shiftKey && event.key !== 'Shift') {\n", - " value += 'shift+';\n", - " }\n", - "\n", - " value += 'k' + event.key;\n", - "\n", - " this._key_event_extra(event, name);\n", - "\n", - " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", - " return false;\n", - "};\n", - "\n", - "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", - " if (name === 'download') {\n", - " this.handle_save(this, null);\n", - " } else {\n", - " this.send_message('toolbar_button', { name: name });\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", - " this.message.textContent = tooltip;\n", - "};\n", - "\n", - "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", - "// prettier-ignore\n", - "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", - "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", - "\n", - "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", - "\n", - "mpl.default_extension = \"png\";/* global mpl */\n", - "\n", - "var comm_websocket_adapter = function (comm) {\n", - " // Create a \"websocket\"-like object which calls the given IPython comm\n", - " // object with the appropriate methods. Currently this is a non binary\n", - " // socket, so there is still some room for performance tuning.\n", - " var ws = {};\n", - "\n", - " ws.binaryType = comm.kernel.ws.binaryType;\n", - " ws.readyState = comm.kernel.ws.readyState;\n", - " function updateReadyState(_event) {\n", - " if (comm.kernel.ws) {\n", - " ws.readyState = comm.kernel.ws.readyState;\n", - " } else {\n", - " ws.readyState = 3; // Closed state.\n", - " }\n", - " }\n", - " comm.kernel.ws.addEventListener('open', updateReadyState);\n", - " comm.kernel.ws.addEventListener('close', updateReadyState);\n", - " comm.kernel.ws.addEventListener('error', updateReadyState);\n", - "\n", - " ws.close = function () {\n", - " comm.close();\n", - " };\n", - " ws.send = function (m) {\n", - " //console.log('sending', m);\n", - " comm.send(m);\n", - " };\n", - " // Register the callback with on_msg.\n", - " comm.on_msg(function (msg) {\n", - " //console.log('receiving', msg['content']['data'], msg);\n", - " var data = msg['content']['data'];\n", - " if (data['blob'] !== undefined) {\n", - " data = {\n", - " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", - " };\n", - " }\n", - " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", - " ws.onmessage(data);\n", - " });\n", - " return ws;\n", - "};\n", - "\n", - "mpl.mpl_figure_comm = function (comm, msg) {\n", - " // This is the function which gets called when the mpl process\n", - " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", - "\n", - " var id = msg.content.data.id;\n", - " // Get hold of the div created by the display call when the Comm\n", - " // socket was opened in Python.\n", - " var element = document.getElementById(id);\n", - " var ws_proxy = comm_websocket_adapter(comm);\n", - "\n", - " function ondownload(figure, _format) {\n", - " window.open(figure.canvas.toDataURL());\n", - " }\n", - "\n", - " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", - "\n", - " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", - " // web socket which is closed, not our websocket->open comm proxy.\n", - " ws_proxy.onopen();\n", - "\n", - " fig.parent_element = element;\n", - " fig.cell_info = mpl.find_output_cell(\"
\");\n", - " if (!fig.cell_info) {\n", - " console.error('Failed to find cell for figure', id, fig);\n", - " return;\n", - " }\n", - " fig.cell_info[0].output_area.element.on(\n", - " 'cleared',\n", - " { fig: fig },\n", - " fig._remove_fig_handler\n", - " );\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_close = function (fig, msg) {\n", - " var width = fig.canvas.width / fig.ratio;\n", - " fig.cell_info[0].output_area.element.off(\n", - " 'cleared',\n", - " fig._remove_fig_handler\n", - " );\n", - " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", - "\n", - " // Update the output cell to use the data from the current canvas.\n", - " fig.push_to_output();\n", - " var dataURL = fig.canvas.toDataURL();\n", - " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", - " // the notebook keyboard shortcuts fail.\n", - " IPython.keyboard_manager.enable();\n", - " fig.parent_element.innerHTML =\n", - " '';\n", - " fig.close_ws(fig, msg);\n", - "};\n", - "\n", - "mpl.figure.prototype.close_ws = function (fig, msg) {\n", - " fig.send_message('closing', msg);\n", - " // fig.ws.close()\n", - "};\n", - "\n", - "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", - " // Turn the data on the canvas into data in the output cell.\n", - " var width = this.canvas.width / this.ratio;\n", - " var dataURL = this.canvas.toDataURL();\n", - " this.cell_info[1]['text/html'] =\n", - " '';\n", - "};\n", - "\n", - "mpl.figure.prototype.updated_canvas_event = function () {\n", - " // Tell IPython that the notebook contents must change.\n", - " IPython.notebook.set_dirty(true);\n", - " this.send_message('ack', {});\n", - " var fig = this;\n", - " // Wait a second, then push the new image to the DOM so\n", - " // that it is saved nicely (might be nice to debounce this).\n", - " setTimeout(function () {\n", - " fig.push_to_output();\n", - " }, 1000);\n", - "};\n", - "\n", - "mpl.figure.prototype._init_toolbar = function () {\n", - " var fig = this;\n", - "\n", - " var toolbar = document.createElement('div');\n", - " toolbar.classList = 'btn-toolbar';\n", - " this.root.appendChild(toolbar);\n", - "\n", - " function on_click_closure(name) {\n", - " return function (_event) {\n", - " return fig.toolbar_button_onclick(name);\n", - " };\n", - " }\n", - "\n", - " function on_mouseover_closure(tooltip) {\n", - " return function (event) {\n", - " if (!event.currentTarget.disabled) {\n", - " return fig.toolbar_button_onmouseover(tooltip);\n", - " }\n", - " };\n", - " }\n", - "\n", - " fig.buttons = {};\n", - " var buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'btn-group';\n", - " var button;\n", - " for (var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " /* Instead of a spacer, we start a new button group. */\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - " buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'btn-group';\n", - " continue;\n", - " }\n", - "\n", - " button = fig.buttons[name] = document.createElement('button');\n", - " button.classList = 'btn btn-default';\n", - " button.href = '#';\n", - " button.title = name;\n", - " button.innerHTML = '';\n", - " button.addEventListener('click', on_click_closure(method_name));\n", - " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", - " buttonGroup.appendChild(button);\n", - " }\n", - "\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - "\n", - " // Add the status bar.\n", - " var status_bar = document.createElement('span');\n", - " status_bar.classList = 'mpl-message pull-right';\n", - " toolbar.appendChild(status_bar);\n", - " this.message = status_bar;\n", - "\n", - " // Add the close button to the window.\n", - " var buttongrp = document.createElement('div');\n", - " buttongrp.classList = 'btn-group inline pull-right';\n", - " button = document.createElement('button');\n", - " button.classList = 'btn btn-mini btn-primary';\n", - " button.href = '#';\n", - " button.title = 'Stop Interaction';\n", - " button.innerHTML = '';\n", - " button.addEventListener('click', function (_evt) {\n", - " fig.handle_close(fig, {});\n", - " });\n", - " button.addEventListener(\n", - " 'mouseover',\n", - " on_mouseover_closure('Stop Interaction')\n", - " );\n", - " buttongrp.appendChild(button);\n", - " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", - " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", - "};\n", - "\n", - "mpl.figure.prototype._remove_fig_handler = function (event) {\n", - " var fig = event.data.fig;\n", - " if (event.target !== this) {\n", - " // Ignore bubbled events from children.\n", - " return;\n", - " }\n", - " fig.close_ws(fig, {});\n", - "};\n", - "\n", - "mpl.figure.prototype._root_extra_style = function (el) {\n", - " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", - "};\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function (el) {\n", - " // this is important to make the div 'focusable\n", - " el.setAttribute('tabindex', 0);\n", - " // reach out to IPython and tell the keyboard manager to turn it's self\n", - " // off when our div gets focus\n", - "\n", - " // location in version 3\n", - " if (IPython.notebook.keyboard_manager) {\n", - " IPython.notebook.keyboard_manager.register_events(el);\n", - " } else {\n", - " // location in version 2\n", - " IPython.keyboard_manager.register_events(el);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", - " // Check for shift+enter\n", - " if (event.shiftKey && event.which === 13) {\n", - " this.canvas_div.blur();\n", - " // select the cell after this one\n", - " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", - " IPython.notebook.select(index + 1);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", - " fig.ondownload(fig, null);\n", - "};\n", - "\n", - "mpl.find_output_cell = function (html_output) {\n", - " // Return the cell and output element which can be found *uniquely* in the notebook.\n", - " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", - " // IPython event is triggered only after the cells have been serialised, which for\n", - " // our purposes (turning an active figure into a static one), is too late.\n", - " var cells = IPython.notebook.get_cells();\n", - " var ncells = cells.length;\n", - " for (var i = 0; i < ncells; i++) {\n", - " var cell = cells[i];\n", - " if (cell.cell_type === 'code') {\n", - " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", - " var data = cell.output_area.outputs[j];\n", - " if (data.data) {\n", - " // IPython >= 3 moved mimebundle to data attribute of output\n", - " data = data.data;\n", - " }\n", - " if (data['text/html'] === html_output) {\n", - " return [cell, data, j];\n", - " }\n", - " }\n", - " }\n", - " }\n", - "};\n", - "\n", - "// Register the function which deals with the matplotlib target/channel.\n", - "// The kernel may be null if the page has been refreshed.\n", - "if (IPython.notebook.kernel !== null) {\n", - " IPython.notebook.kernel.comm_manager.register_target(\n", - " 'matplotlib',\n", - " mpl.mpl_figure_comm\n", - " );\n", - "}\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "9c613fb7abf54461a575817c30e50706", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "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F19UbGysJMfhVyNHjpQknT9/Xu+//76mTZumbt26udTfr18/LViwQI888kilHgcAAAA4+OQekMTERG3dulW33367srKy1KhRI61cudLbZVUfVqvj8KqiS9EQUTJWdALVlXKr4PArq9WqxMREBQYG6sSJExo/frx2796tqKgopaWlKTk5Wa1atZLk+BjmkgNIs2bN1KxZM1mtVrVo0cL5RY+tWrVSVlaWM2/69Olq06aNOnXqpMOHD+vw4cOSpMmTJ+v//b//p3Pnzmno0KGSpJycHG3ZskV33323JMfJ6CdOnNBDDz2kli1bulz69++v1KK9QgAAAKg0nxxABg8erHPnzmngwIG65ZZbdO211+qaa67xdlmogMzMTDVv3lwhISEKDw/Xrl271LZtWw0cOFBpaWnauXOny/khW7dudRlASn5KWEBAgPN6QECACgsLJTm+N+XLL7/UV1995by/oo83zsnJUUFBgfOLIiVp2bJlSkpKUkxMjCTHHreUlJRSP2mtf//+2rJli3bs2FG1DwwAAICf8skBZPfu3YqIiNCePXv0xz/+UZ9//rkeffRRb5eFChg7dqx27twpyXFyeXh4uIYOHark5GTl5+crOjraeX7PqlWrtHr16nJ9YpbkOMyrTp06Cg0N1fbt25WZmem8bcSIEZoxY4bat2+vadOmSbr406+WLVumTz/9tNR1JyUlyRhT7poAAABQOp88ByQoKEjGGM2bN0/PPfechgwZonbt2nm7LFTS5MmTtWnTJoWFhalTp04aMGCAjh8/rp49e+qmm25Sy5YtFR8fr6uvvrpc6+3Ro4dmzZqlhIQEJSYmOp8rqampqlu3rnr16qUuXbooKSlJffv21W233abBgwe7YxMBAABwBT45gDzyyCO6+eabdfLkSb300kuSpF9//dW7RaHS3nvvvYtidevW1datW0vNb9SokbZs2eK8/s9//tP586233qrly5dLchym9dlnn120fMuWLfXQQw9JkmrXrq3du3dLksaPH1/xjQAAAECleO0QrKFDh+rs2bOS5DxhuMiIESO0du1a7dy5U7Vr19aBAwd06623eqNMAAAAAFXIa3tAateurfz8fNWqVUuNGjXS1VdfrdatW6tt27Zq06aNbrrpJiUkJEiSmjZtqvnz53urVAAAAABVxGsDyOzZs50/Hzx4UJmZmc4TiJcuXaqsrCwFBgaqefPmLicVAwAAAKi+fOIckPj4eMXHx7t8MtGpU6e0fft2Pv4UAAAAqEF8YgApTXh4uG6//Xbdfvvt3i4FAAAAQBXxye8BwaUZY7xdAi5ATwAAAMqOAaSaCAoKkiSdOXPGy5XgQkU9KeoRAAAALs1nD8GCK6vVqqioKOXk5EiSwsLCZLFYvFyVfzPG6MyZM8rJyVFUVJSsVqu3SwIAAPB5DCDVSGxsrCQ5hxD4hqioKGdvAAAAcHkMINWIxWJR/fr1VbduXdlsNm+XAzkOu2LPBwAAQNkxgFRDVquVX3oBAABQLXESOgAAAACPYQABAAAA4DEMIAAAAAA8hgEEAAAAgMcwgAAAAADwGAYQAAAAAB7DAAIAAADAYxhAAAAAAHiM3w8gWVlZeuihh9S4cWPVqlVLTZo00cSJE1VQUOCSY7FYLrps2rTJZV0fffSRmjdvrtDQULVq1UorVqzw9OYAAAAAPs3vvwn9m2++kd1u1zvvvKOmTZtq165dGjFihH799Ve9/vrrLrmrV69WYmKi83qdOnWcP2/cuFGDBw/WlClTdPfdd+vDDz9Uv379tG3bNrVs2dJj2wMAAAD4Mr8fQHr06KEePXo4r19//fXat2+fZs2addEAUqdOHcXGxpa6nrfeeks9evTQuHHjJEmTJk1Senq6Zs6cqdmzZ7tvAwAAAIBqxO8HkNLk5uYqOjr6onifPn107tw53XjjjRo/frz69OnjvC0jI0NPPfWUS3737t2VlpZ2yfvJz89Xfn6+83peXp4kyWazyWazVXIr4OuKekyv/QP99i/027/Qb/9CnyuPAeQCBw4c0IwZM1z2flx11VWaNm2afvOb3yggIED/+te/1K9fP6WlpTmHkOzsbNWrV89lXfXq1VN2dvYl72vKlCl6+eWXL4qvXbtWYWFhVbRF8HXp6eneLgEeRL/9C/32L/TbP5w5c8bbJVR7FmOM8XYR7vDcc8/ptddeu2zO3r171bx5c+f1n376ScnJyerSpYv+/ve/X3bZYcOG6eDBg/riiy8kScHBwXrvvfc0ePBgZ87f/vY3vfzyyzp27Fip6yhtD0jDhg119OhRl/NLUDPZbDalp6era9euCgoK8nY5cDP67V/ot3+h3/4lLy9PMTExys3NVUREhLfLqZZq7B6Qp59+Wvfff/9lc66//nrnz0eOHNEdd9yhTp06ac6cOVdcf4cOHVz+0hEbG3vRoHHs2LFLnjMiSSEhIQoJCbkoHhQUxBuYH6Hf/oV++xf67V/ot3+gx5VXYweQa665Rtdcc02Zcn/66SfdcccdateunebNm6eAgCt/OvH27dtVv3595/WOHTtqzZo1euKJJ5yx9PR0dezYsdy1AwAAADVVjR1Ayuqnn35Sly5dFB8fr9dff10///yz87aivRfvvfeegoODddNNN0mSPv74Y82dO9flMK3HH39cycnJmjZtmnr16qWFCxdqy5YtZdqbAgAAAPgLvx9A0tPTdeDAAR04cEANGjRwua3k6TGTJk3SoUOHFBgYqObNm2vRokW65557nLd36tRJH374oV544QVNmDBBN9xwg9LS0vgOEAAAAKAEvx9A7r///iueKzJ8+HANHz78iusaMGCABgwYUEWVAQAAADXPlU92AAAAAIAqwgACAAAAwGMYQAAAAAB4DAMIAAAAAI9hAAEAAADgMQwgAAAAADyGAQQAAACAxzCAAAAAAPAYBhAAAAAAHsMAAgAAAMBjGEAAAAAAeAwDCAAAAACPYQABAAAA4DEMIAAAAAA8JtDbBaAUNpvjIklWqxQQUHy9SFCQVFgo2e3FsfLkBgQ48kvLtdsd+VWZGxgoGVN67vnzjtsul2uxOOKVzZUc8SvlWq2O2yqbW1qPinJtNln+92+l+1mTe19VzxOpbP2squfJhY+73e5YtuTjU5bnSUk8Ty6d62vvESVf3xV9j7jwsaT3vvseUfTzhX2rbO95nhTzpfeIC2tD+Rn4jNzcXCPJ5Dqe7o7LBx84bgwMLI7FxztiU6cWxyRj5sxxxCMji2MxMY7YzJmuudOnO+JxccWxsDBHbN4819zJkx3xpk1d48YYs3ixa2zCBEe8TRvX+OnTxixf7hp7/HFHbqdOrvGcHGPWrnWNPfywIzclxTV+8KAxmze7xu6915Hbt69rfNcux6VkrG9fR+6997rGN292rLtkLCXFkfvww67xtWsdNZeMderkyH38cdf48uWOx6JEzN66tSN3wgTX3MWLHfGSsaZNHbHJk13j8+Y54mFhxbG4OEds+nTX3JkzHfGYmOJYZKQjNmeOa+7UqY54fHxxLDDQEfvgA9fciRMd8YQE13hBgTGffOIae+YZR+4tt7jGT5wwZtUq19gf/uDITU52jf/4ozEbNrjGhg935N51l2v822+N+fpr19g99zhy77nHNf711478krG77nLkDh/uGt+wwVFHyVhysiP3D39widtWrDDLL3zMbrnFkfvMM67xTz5xPG4lYwkJjtyJE13jvEc4LjX4PcK0aePI5T3CZ98jCnv2NGlpaaZw6FDX3HK8R5hVqxzbVzLGe0TxxYfeI3IlI8nk5uYaVIzFGGO8PQTBIS8vT5GRkTp+9Kjq1KnjCPKXCwdf++tmWXMv81crm82mlStXqmfPngoKC+OvVlWR68N7QGx2u1asXKm7unVTUFDQZXP56+YFudXwPcJWUFD8+g4N5S/bl8utAe8RtvPntWLVKsfru+h2iT0gVZnrQ+8Reb/8osiYGOXm5ioiIkIoPw7B8kVBQY7LhbELWa2OS2nLVyY3IMBxqepci6X03MBSnobuyr1Ube7KvcLjbgIDi5dzVz/p/eVr81TvbTZHvaW9vn2h9zxPLl9beXONKX59F923r/aT3l++tvLmlhb31X7yPCl/7qVqQ7lwEjoAAAAAj2EAAQAAAOAxDCAAAAAAPIYBBAAAAIDHMIAAAAAA8BgGEAAAAAAewwACAAAAwGP4HhAfUvSdkKdOnSr+ojLUWDabTWfOnFFeXh799gP027/Qb/9Cv/1LXl6epOLf21B+DCA+5L///a8kqXHjxl6uBAAAAJdz6tQpRUZGeruMaokBxIdER0dLkg4fPswT2g/k5eWpYcOG+uGHHxQREeHtcuBm9Nu/0G//Qr/9izFGp06dUlxcnLdLqbYYQHxIQIDjlJzIyEjewPxIREQE/fYj9Nu/0G//Qr/9B38orhxOQgcAAADgMQwgAAAAADyGAcSHhISEaOLEiQoJCfF2KfAA+u1f6Ld/od/+hX4D5WMxfIYYAAAAAA9hDwgAAAAAj2EAAQAAAOAxDCAAAAAAPIYBBAAAAIDHMIC42dtvv61GjRopNDRUHTp00ObNmy+b/9FHH6l58+YKDQ1Vq1attGLFCpfbjTF68cUXVb9+fdWqVUspKSnav3+/OzcB5VCV/bbZbHr22WfVqlUr1a5dW3FxcRo2bJiOHDni7s1AGVX167ukRx55RBaLRW+++WYVV42Kcke/9+7dqz59+igyMlK1a9dW+/btdfjwYXdtAsqhqvt9+vRpjR07Vg0aNFCtWrWUkJCg2bNnu3MTAN9l4DYLFy40wcHBZu7cuWb37t1mxIgRJioqyhw7dqzU/C+//NJYrVYzdepUs2fPHvPCCy+YoKAgs3PnTmfOq6++aiIjI01aWprJzMw0ffr0MY0bNzZnz5711GbhEqq63ydPnjQpKSlm0aJF5ptvvjEZGRkmKSnJtGvXzpObhUtwx+u7yMcff2zatGlj4uLizPTp0928JSgLd/T7wIEDJjo62owbN85s27bNHDhwwCxZsuSS64TnuKPfI0aMME2aNDFr1641Bw8eNO+8846xWq1myZIlntoswGcwgLhRUlKSGTNmjPN6YWGhiYuLM1OmTCk1f+DAgaZXr14usQ4dOphRo0YZY4yx2+0mNjbW/PWvf3XefvLkSRMSEmIWLFjghi1AeVR1v0uzefNmI8kcOnSoaopGhbmr3z/++KO59tprza5du0x8fDwDiI9wR78HDRpkhgwZ4p6CUSnu6HdiYqJ55ZVXXHJuvvlm88c//rEKKweqBw7BcpOCggJt3bpVKSkpzlhAQIBSUlKUkZFR6jIZGRku+ZLUvXt3Z/7BgweVnZ3tkhMZGakOHTpccp3wDHf0uzS5ubmyWCyKioqqkrpRMe7qt91u19ChQzVu3DglJia6p3iUmzv6bbfb9emnn+rGG29U9+7dVbduXXXo0EFpaWlu2w6Ujbte3506ddLSpUv1008/yRijtWvX6ttvv1W3bt3csyGAD2MAcZPjx4+rsLBQ9erVc4nXq1dP2dnZpS6TnZ192fyif8uzTniGO/p9oXPnzunZZ5/V4MGDFRERUTWFo0Lc1e/XXntNgYGBeuyxx6q+aFSYO/qdk5Oj06dP69VXX1WPHj20atUq/e53v9Pvf/97rVu3zj0bgjJx1+t7xowZSkhIUIMGDRQcHKwePXro7bffVufOnat+IwAfF+jtAgBcmc1m08CBA2WM0axZs7xdDtxg69ateuutt7Rt2zZZLBZvlwM3s9vtkqS+ffvqySeflCS1bdtWGzdu1OzZs5WcnOzN8uAGM2bM0KZNm7R06VLFx8dr/fr1GjNmjOLi4i7aewLUdOwBcZOYmBhZrVYdO3bMJX7s2DHFxsaWukxsbOxl84v+Lc864Rnu6HeRouHj0KFDSk9PZ++HD3BHv7/44gvl5OTouuuuU2BgoAIDA3Xo0CE9/fTTatSokVu2A2Xjjn7HxMQoMDBQCQkJLjktWrTgU7C8zB39Pnv2rCZMmKA33nhDvXv3VuvWrTV27FgNGjRIr7/+uns2BPBhDCBuEhwcrHbt2mnNmjXOmN1u15o1a9SxY8dSl+nYsaNLviSlp6c78xs3bqzY2FiXnLy8PH311VeXXCc8wx39loqHj/3792v16tWqU6eOezYA5eKOfg8dOlQ7duzQ9u3bnZe4uDiNGzdO//73v923Mbgid/Q7ODhY7du31759+1xyvv32W8XHx1fxFqA83NFvm80mm82mgADXX7usVqtzbxjgV7x9FnxNtnDhQhMSEmLmz59v9uzZY0aOHGmioqJMdna2McaYoUOHmueee86Z/+WXX5rAwEDz+uuvm71795qJEyeW+jG8UVFRZsmSJWbHjh2mb9++fAyvj6jqfhcUFJg+ffqYBg0amO3bt5ujR486L/n5+V7ZRhRzx+v7QnwKlu9wR78//vhjExQUZObMmWP2799vZsyYYaxWq/niiy88vn1w5Y5+Jycnm8TERLN27Vrz/fffm3nz5pnQ0FDzt7/9zePbB3gbA4ibzZgxw1x33XUmODjYJCUlmU2bNjlvS05ONsOHD3fJX7x4sbnxxhtNcHCwSUxMNJ9++qnL7Xa73fzpT38y9erVMyEhIebOO+80+/bt88SmoAyqst8HDx40kkq9rF271kNbhMup6tf3hRhAfIs7+p2ammqaNm1qQkNDTZs2bUxaWpq7NwNlVNX9Pnr0qLn//vtNXFycCQ0NNc2aNTPTpk0zdrvdE5sD+BSLMcZ4cw8MAAAAAP/BOSAAAAAAPIYBBAAAAIDHMIAAAAAA8BgGEAAAAAAewzeh+xC73a4jR44oPDycb0IGAADwQcYYnTp1SnFxcRd9twvKhgHEhxw5ckQNGzb0dhkAAAC4gh9++EENGjTwdhnVEgOIDwkPD5ckHTx4UNHR0V6uBu5ms9m0atUqdevWTUFBQd4uB25Gv/0L/fYv9Nu/5OXlqWHDhs7f21B+DCA+pOiwq/DwcEVERHi5GribzWZTWFiYIiIi+A/LD9Bv/0K//Qv99k8cLl9xHLgGAAAAwGMYQAAAAAB4DAMIAAAAAI/hHJBqqLCwUDabzdtlQFJQUJCsVqu3ywAAAKg2GECqEWOMsrOzdfLkSW+XghKioqIUGxvLyWgAAABlwABSjRQNH3Xr1lVYWBi/8HqZMUZnzpxRTk6OJKl+/fperggAAMD3MYBUE4WFhc7ho06dOt4uB/9Tq1YtSVJOTo7q1q3L4VgAAABXwADii2w2x0WSrFYpIEC2s2clYxQWGirZ7VJAgGSM41LEYnFc7HbX9flCbtHemtJyS1tHNcoN+98QYjt7VtaQkOLcwECpsND1sbBaHbedPy/ZbLL8718FBZWeGxBQ/FwoUtncgABHfmm5drsjvypzAwMdj1dpuefPuz7Glc0tetxLy5Uc8arMLdnPC3MvfNztdseyJR+fsjxPSuJ5cuncy/XTHc+Ty/X+wtd3eZ4n9N41t7q8RxT9fGHfKtt7nifFfOk9gvNwK8/AZ+Tm5hpJJrf4V1xjPvjAGGPM2SZNzJ6VK83Z//zHmMxMxwJHjxrzn/8UX3JyHPFt24pjX3/tiB075pqbne2Ib99eHNu61RH7+WfX3CNHHPEdO1zjxhjz3/+6xn780RHftcs1fv68MSdOuMYOHXLk7t3rGi8oMCYvzzV28KAjd98+1/i5c8acPu0a++47R+7+/a7xM2ccl5Kx/fsdud995xo/fdqx7pKxffscuQcPusTPHj9u9uzcac7Gxxf3rVMnR+7jjxfHJGOWL3esu0TM3rq1I3fCBNfcxYsd8ZKxpk0dscmTXePz5jniYWHFsbg4R2z6dNfcmTMd8ZiY4lhkpCM2Z45r7tSpjnjJbQsMdMQ++MA1d+JERzwhwTVeUGDMJ5+4xp55xpF7yy2u8RMnjFm1yjX2hz84cpOTXeM//mjMhg2useHDHbl33eUa//Zbx2uhZOyeexy599zjGv/6a0d+ydhddzlyhw93jW/Y4KijZCw52ZH7hz+4xG0rVpjlFz5mt9ziyH3mGdf4J584HreSsYQER+7Eia7x/71HmMDA4lh8vCM2dapr7pw5jnhkZHEsJsYRmznTNXf6dEc8Lq44FhbmiM2b55o7ebIj3rSpa9wYx/O4ZGzCBEe8TRvX+OnTjtdHydjjjztyO3VyjefkGLN2rWvs4YcduSkprvGDB43ZvNk1du+9jty+fV3ju3Y5LiVjffs6cu+91zW+ebNj3SVjKSmO3Icfdo2vXeuouWSsHO8Rpk0bRy7vET77HlHYs6dJS0szhUOHuuaW4z3CrFrl2L6SMd4jii8+9B6RKxlJJjc316BiLMYY4+0hCA55eXmKjIzU8aNHiw+z+t9fI86dOqWDP/ygxo0aKTQ01Df2arAHRJJ0Lj9fB7Oy1LhBA0dvinKv8Fcrm82mlStXqmfPngoKC+OvVlWR68N7QGx2u1asXKm7unUr/qZk/rpZzJf+ulkFe0BsBQXFr+/QUP6yfbncGvAeYTt/XitWrXK8votul9gDUpW5PvQekffLL4qMiVFubq4iIiKE8uMQLF8UFOS4XBizWBwvtoD/fX1L0S/EFyq6vSRfyC3KL+s6qktu0c+l9c1qdVwu9L88ExhYvMwVcsuz3grnlnx+VWVu0XP3QoGlvAW5K/dStbkr98LH3WZz1FuB54nP5fI8uXKuMcWv76L79tV+0vvL11be3NLivtpPniflz71UbSgXvogQHjFq1Cjdd9993i4DAAAAXsYAAo+YMmWK5syZU+HlH3jgAb3wwgsusYyMDFmtVvXq1auy5QEAAMBDGEDgEdHR0apdu3aFli0sLNTy5cvVp08fl3hqaqoeffRRrV+/XkeOHKmKMgEAAOBmDCBwu6ysLFksFmVlZVVo+Y0bNyooKEjt27d3xk6fPq1FixZp9OjR6tWrl+bPn181xQIAAMCtGEDgdpmZmYqKilKjRo0qtPzSpUvVu3dvlfzm98WLF6t58+Zq1qyZhgwZorlz54oPdAMAAPB9DCBwu+3bt6t169YVXn7JkiWlHn41ZMgQSVKPHj2Um5urdevWVapOAAAAuB8fw1vDbMn6RVP//Y1OnTt/5eQyCA8N0vjuzXRLo+gKryMzM1Nt27at0LJ79+7VkSNHdOeddzpj+/bt0+bNm/XJJ59IkgIDAzVo0CClpqaqS5cuFa4TAAAA7scAUsO8+8X32nzwRJWvszIDyPbt23X33Xc71vXuu5o1a5YKCgqUmJioRYsWaffu3Ro5cqROnjypbt26aceOHVqzZo0kx+FXXbt2Lf6CPzn2fpw/f15xcXHOmDFGISEhmjlzpiIjIytcKwAAANyLAaSGGXH79TpxpqBK94CMuP36Ci+fl5enrKwstW3bVidOnNDbb7+trVu3ymq16uTJk8rPz9fgwYO1cOFCJSQkqF+/fi6Hay1ZskQjR450Xj9//rzef/99TZs2Td26dXO5r379+mnBggV65JFHKlwvAAAA3IsBpIa5pVG0Fo/q5O0ynDIzM2W1WpWYmKiCggKdOHFC48eP14MPPujcA9K5c2clJCRIkpo1a6ZmzZpJknJycrRlyxYtXbrUub7ly5frxIkTeuihhy7a09G/f3+lpqYygAAAAPgwTkKHW2VmZqp58+YKCQlReHi4du3apbZt22rgwIFKS0vTzp071aZNG5f8oj0gy5YtU1JSkmJiYpy3p6amKiUlpdTDrPr3768tW7Zox44d7t8wAAAAVAh7QOBWY8eO1dixYyVJ+/fv1w033KChQ4cqIyND+fn5io6O1r59+yRJK1as0Jo1a5wnl5f26VfLli275H0lJSXxUbwAAAA+jgEEHjN58mRt2rRJYWFh6tSpkwYMGKDjx4/rrrvuUps2bdSnTx81bdpUtWrVkiTddtttGjx4sJerBgAAQFViAIHHvPfeexfF6tatqy1btkiSvvrqK+3du9d52/jx4z1WGwAAADyDc0AkTZkyRe3bt1d4eLjq1q2rfv36OQ8LKtKlSxdZLBaXy4UnOx8+fFi9evVSWFiY6tatq3Hjxun8+ar5NCp/sGPHjkp9YSEAAAB8H3tAJK1bt05jxoxR+/btdf78eU2YMEHdunXTnj17VLt2bWfeiBEj9Morrzivh4WFOX8uLCxUr169FBsbq40bN+ro0aMaNmyYgoKC9Je//MWj21NdjRgxwtslAAAAwM0YQCR99tlnLtfnz5+vunXrauvWrercubMzHhYWptjY2FLXsWrVKu3Zs0erV69WvXr11LZtW02aNEnPPvusXnrpJQUHB7t1GwAAAIDqgEOwSpGbmytJio52/fbvf/zjH4qJiVHLli31/PPP68yZM87bMjIy1KpVK9WrV88Z6969u/Ly8rR7927PFA4AAAD4OPaAXMBut+uJJ57Qb37zG7Vs2dIZv/feexUfH6+4uDjt2LFDzz77rPbt26ePP/5YkpSdne0yfEhyXs/Ozi71vvLz85Wfn++8npeXJ0my2Wyy2WwuuTabTcYY2e122e32ym8oqozdbpcxRjabTVartczLFfX4wl6jZqLf/oV++xf67V/oc+UxgFxgzJgx2rVrlzZs2OASHzlypPPnVq1aqX79+rrzzjv13XffqUmTJhW6rylTpujll1++KL527VqX80skKTAwULGxsTp9+rQKCgoqdH9wj4KCAp09e1br16+v0IcOpKenu6Eq+Cr67V/ot3+h3/6h5BEwqBgGkBLGjh2r5cuXa/369WrQoMFlczt06CBJOnDggJo0aaLY2Fht3rzZJefYsWOSdMnzRp5//nk99dRTzut5eXlq2LCh7rjjDtWpU8cl99y5c/rhhx901VVXKTQ0tNzbBvc5d+6catWqpc6dO5erNzabTenp6eratauCgoLcWCF8Af32L/Tbv9Bv/1J0xAoqjgFEkjFGjz76qD755BN9/vnnaty48RWX2b59uySpfv36kqSOHTvqz3/+s3JyclS3bl1Jjr+EREREKCEhodR1hISEKCQk5KJ4UFDQRW9ghYWFslgsCggIUEAAp+74koCAAFksllL7VhYVXQ7VE/32L/Tbv9Bv/0CPK48BRI7Drj788EMtWbJE4eHhznM2IiMjVatWLX333Xf68MMPddddd6lOnTrasWOHnnzySXXu3Nn5vRXdunVTQkKChg4dqqlTpyo7O1svvPCCxowZU+qQAQAAAPgj/pQuadasWcrNzVWXLl1Uv35952XRokWSpODgYK1evVrdunVT8+bN9fTTT6t///5atmyZcx1Wq1XLly+X1WpVx44dNWTIEA0bNszle0MAAAAAf8ceEDkOwbqchg0bat26dVdcT3x8vFasWFFVZQEAAAA1DntAAAAAAHgMAwgAAAAAj2EAgc8rOkTupZdecrkOAACA6odzQODzZs2apcDAQP3666967rnn1LNnTyUnJ3u7LAAAAFQAe0DgEaNGjdJ9991XoWX/8Ic/KDc3V//3f/+n3r17M3wAAABUYwwg8IgpU6Zozpw5FVp29uzZioyM1GOPPaZly5bpiy++KDXvgQce0AsvvOASy8jIkNVqVa9evSp03wAAAKhaDCDwiOjoaNWuXbtCy44aNUojR45U7dq19eqrr+q22267KKewsFDLly9Xnz59XOKpqal69NFHtX79eh05cqRC9w8AAICqwwACt8vKypLFYlFWVlaFlrdYLJKKT0Ivul7Sxo0bFRQUpPbt2ztjp0+f1qJFizR69Gj16tVL8+fPr9D9AwAAoOowgMDtMjMzFRUVpUaNGrntPpYuXarevXu7DCeLFy9W8+bN1axZMw0ZMkRz587lE7QAAAC8jE/BqmkOb5JWvyzln6qa9YVGSHe+KF13a4VXsX37drVu3bpq6rmEJUuWaPr06S6x1NRUDRkyRJLUo0cP5ebmat26derSpYtbawEAAMClMYDUNBtnSIc3Vv06KzGAZGZmqm3btlVXzwX27t2rI0eO6M4773TG9u3bp82bN+uTTz6R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eOABb5fmddHR0d4u4SKFhYVavny5Pv30U2csNTVVjz76qFJTU3XkyBHFxcV5sUIAAICax6f2gERGRmrfvn3O63fddZeWLVumpKQkPfzww16srGwGDRqk119/XS+++KLatm2r7du367PPPrvoxHR/k5WVJYvFoqysLG+X4mLjxo0KCgpS+/btJUmnT5/WokWLNHr0aPXq1Uvz58/3boEAAAA1kE/tAXnnnXc0cOBAJScnq02bNvrmm28UGOhTJV7R2LFjNXbsWG+X4VMyMzMVFRWlRo0aebsUF0uXLlXv3r1lsVgkSYsXL1bz5s3VrFkzDRkyRE888YSef/555+0AAACoPJ/aA5KYmKj//Oc/uu2225SVlaVrr71WK1as8HZZqKTt27erdevW3i7jIkuWLFGfPn2c11NTUzVkyBBJUo8ePZSbm6t169Z5qzwAAIAayacGkMGDB+vcuXMaOHCgbrnlFl177bW65pprvF1W9VNYKNlsxZeiLy4sGSv6Ep0r5RYWVrqczMxMtW3bttLrqUp79+7VkSNHdOedd0qS9u3bp82bN2vw4MGSHN+7MmjQIKWmpnqzTAAAgBrHpwaQ3bt3KyIiQnv27NEf//hHff7553r00Ue9XVb1M2mSFBxcfFmwwBEPCyuO3XCDI/bGG665Rb9wX3ON4/qkSZUuZ/v27WrTpo0k6d1339XNN9+sli1batCgQZIcff/Nb36jxMREPfnkk86hICsrS23atNF9992nG264QaNHj1ZaWpo6dOigli1bav/+/c77uPvuu9WuXTu1bNnS+VG/GRkZSkpK0vnz53Xs2DHdcMMNys7OluQ4/Kpr167O7+1ITU3V+fPnFRcXp8DAQAUGBmrWrFn617/+pdzc3Eo/BgAAAHDwqRMsgoKCZIzRvHnz9Nxzz2nIkCFq166dt8uqfv70J+mPfyy+brU6/i3tmzufekp64omLc3/+2fFvJb9zJC8vT1lZWWrbtq1OnDiht99+W1u3bpXVatXJkyeVn5+vwYMHa+HChUpISFC/fv1cDtfau3evFi9erKZNm6ply5a66qqr9NVXX+mdd97RzJkz9dZbb0mS3n//fUVHR+vXX39V+/btdc8996hjx47q3LmzXnvtNX399dd68cUXnd/JsmTJEo0cOVKSdP78eb3//vuaNm2aunXr5lJ/v379tGDBAj3yyCOV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AFTDBpAPPvhAKSkpCgsL02OPPabHHntMtWrV0p133qkPP/zQ2+UBAAAAfq9GnQPy5z//WVOnTtWTTz7pjD322GN64403NGnSJN17771erA4AAABAjdoD8v3336t3794Xxfv06aODBw96oSIAAAAAJdWoAaRhw4Zas2bNRfHVq1erYcOGXqgIAAAAQEk16hCsp59+Wo899pi2b9+uTp06SZK+/PJLzZ8/X2+99ZaXqwMAAABQowaQ0aNHKzY2VtOmTdPixYslSS1atNCiRYvUt29fL1cHAAAAoEYNIJL0u9/9Tr/73e+8XQYAAACAUtSoc0AAAAAA+LZqvwckOjpa3377rWJiYnT11VfLYrFcMveXX37xYGUAAAAALlTtB5Dp06crPDzc+fPlBhAAAAAA3lXtB5Dhw4c7f77//vu9VwgAAACAK6pR54BYrVbl5ORcFP/vf/8rq9XqhYoAAAAAlFSjBhBjTKnx/Px8BQcHe7gaAAAAABeq9odgSdL//d//SZIsFov+/ve/66qrrnLeVlhYqPXr16t58+beKg8AAADA/9SIAWT69OmSHHtAZs+e7XK4VXBwsBo1aqTZs2d7qzwAAAAA/1MjBpCDBw9Kku644w59/PHHuvrqq71cEQAAAIDS1IgBpMjatWu9XQIAAACAy6hRJ6H3799fr7322kXxqVOnasCAAV6oCAAAAEBJNWoAWb9+ve66666L4j179tT69eu9UBEAAACAkmrUAHL69OlSP243KChIeXl5XqgIAAAAQEk1agBp1aqVFi1adFF84cKFSkhI8EJFAAAAAEqqUSeh/+lPf9Lvf/97fffdd/rtb38rSVqzZo0WLFigjz76qMzrmTJlij7++GN98803qlWrljp16qTXXntNzZo1u+Qy8+fP1wMPPOASCwkJ0blz5yq2MQAAAEANVKMGkN69eystLU1/+ctf9M9//lO1atVS69attXr1aiUnJ5d5PevWrdOYMWPUvn17nT9/XhMmTFC3bt20Z88e1a5d+5LLRUREaN++fc7rFoulUtsDAAAA1DQ1agCRpF69eqlXr14XxXft2qWWLVuWaR2fffaZy/X58+erbt262rp1qzp37nzJ5SwWi2JjY8tXMAAAAOBHatwAUtKpU6e0YMEC/f3vf9fWrVtVWFhYofXk5uZKkqKjoy+bd/r0acXHx8tut+vmm2/W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", "text/html": [ - "" + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " ], "text/plain": [ - "" + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" ] }, "metadata": {}, @@ -1283,7 +331,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -1341,7 +389,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -1383,7 +431,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -1405,7 +453,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -1438,7 +486,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -1474,7 +522,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -1535,7 +583,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -1570,7 +618,7 @@ "source": [ "from gym_electric_motor.visualization import MotorDashboard\n", "\n", - "visualization = MotorDashboard(state_plots=['i_sq', 'i_sd', 'cos(epsilon)'], reward_plot=True) \n", + "visualization = MotorDashboard(state_plots=['i_sq', 'i_sd', 'cos(epsilon)', 'i_abs'], reward_plot=True) \n", "# plots the states i_sd and i_sq and reward." ] }, @@ -1649,7 +697,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -1668,6 +716,106 @@ "]" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2.8.1 Custom State Action Processor\n", + "A custom state action processor has to inherit from the base ``StateActionProcessor`` class. \n", + "\n", + "In the next cell an example of a user-defined processor is shown. \n", + "\n", + "In the ``__init__()`` method, additional fields can be set. \n", + "\n", + "The ``reset()`` method has to reset the processor and the inner system. Furthermore it has to return the initial state of the system after the reset.\n", + "\n", + "The ``simulate(action)`` method simulates one step of the physical system. " + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "import gym\n", + "import numpy as np\n", + "\n", + "from gym_electric_motor.state_action_processors import StateActionProcessor\n", + "\n", + "\n", + "class CurrentVectorProcessor(StateActionProcessor):\n", + " \"\"\"Adds an ``i_abs`` state to the systems state vector that is the root of the squared sum of the currents ``i_sd`` and `` i_sq``.\"\"\"\n", + "\n", + " def __init__(self, physical_system=None):\n", + " self._i_sd_idx = None\n", + " self._i_sq_idx = None\n", + " super().__init__(physical_system=physical_system)\n", + "\n", + " def set_physical_system(self, physical_system):\n", + " \"\"\"Writes basic data like state indices to locals and finally initializes the instance.\n", + " \n", + " Args:\n", + " physical_system(PhysicalSystem): The inner physical system of the processor.\n", + " \n", + " Returns:\n", + " this: This instance.\n", + " \"\"\"\n", + " super().set_physical_system(physical_system)\n", + " # Define the new state space as concatenation of the old state space and [-1,1] for i_abs\n", + " low = np.concatenate((physical_system.state_space.low, [-1.]))\n", + " high = np.concatenate((physical_system.state_space.high, [1.]))\n", + " self.state_space = gym.spaces.Box(low, high, dtype=np.float64)\n", + "\n", + " # Set the new limits / nominal values of the state vector\n", + " self._i_sq_idx = self._physical_system.state_names.index('i_sq')\n", + " self._i_sd_idx = self._physical_system.state_names.index('i_sq')\n", + " current_limit = np.sqrt((physical_system.limits[self._i_sd_idx]**2 + physical_system.limits[self._i_sq_idx]**2) / 2)\n", + " current_nominal_value = np.sqrt((physical_system.nominal_state[self._i_sd_idx]**2 + physical_system.nominal_state[self._i_sq_idx]**2)/2)\n", + " self._limits = np.concatenate((physical_system.limits, [current_limit]))\n", + " self._nominal_state = np.concatenate((physical_system.nominal_state, [current_nominal_value]))\n", + "\n", + " # Append the new state to the state name vector and the state positions dictionary\n", + " self._state_names = physical_system.state_names + ['i_abs']\n", + " self._state_positions = physical_system.state_positions.copy()\n", + " self._state_positions['i_abs'] = self._state_names.index('i_abs')\n", + " return self\n", + "\n", + " def reset(self):\n", + " \"\"\"Resets this instance and the inner system\n", + " \n", + " Returns:\n", + " np.ndarray: The initial state after the reset.\"\"\"\n", + " state = self._physical_system.reset()\n", + " return np.concatenate((state, [self._get_current_abs(state)]))\n", + "\n", + " def simulate(self, action):\n", + " \"\"\"Simulates one step of the system.\"\"\"\n", + " state = self._physical_system.simulate(action)\n", + " return np.concatenate((state, [self._get_current_abs(state)]))\n", + "\n", + " def _get_current_abs(self, state):\n", + " \"\"\"Calculates the root sum of the squared currents from the state\n", + "\n", + " Args:\n", + " state(numpy.ndarray[float]): The state of the inner system.\n", + "\n", + " Returns:\n", + " float: The rms of the currents of the state.\n", + " \"\"\"\n", + " return np.sqrt(state[self._i_sd_idx]**2 + state[self._i_sq_idx]**2)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "state_action_processors.append(CurrentVectorProcessor())" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1683,7 +831,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -1725,7 +873,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -1764,991 +912,29 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 17, "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\arnet\\AppData\\Roaming\\Python\\Python39\\site-packages\\numpy\\core\\fromnumeric.py:3419: RuntimeWarning: Mean of empty slice.\n", - " return _methods._mean(a, axis=axis, dtype=dtype,\n", - "C:\\Users\\arnet\\AppData\\Roaming\\Python\\Python39\\site-packages\\numpy\\core\\_methods.py:188: RuntimeWarning: invalid value encountered in double_scalars\n", - " ret = ret.dtype.type(ret / rcount)\n" - ] - }, - { - "data": { - "application/javascript": [ - "/* Put everything inside the global mpl namespace */\n", - "/* global mpl */\n", - "window.mpl = {};\n", - "\n", - "mpl.get_websocket_type = function () {\n", - " if (typeof WebSocket !== 'undefined') {\n", - " return WebSocket;\n", - " } else if (typeof MozWebSocket !== 'undefined') {\n", - " return MozWebSocket;\n", - " } else {\n", - " alert(\n", - " 'Your browser does not have WebSocket support. ' +\n", - " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", - " 'Firefox 4 and 5 are also supported but you ' +\n", - " 'have to enable WebSockets in about:config.'\n", - " );\n", - " }\n", - "};\n", - "\n", - "mpl.figure = function (figure_id, websocket, ondownload, parent_element) {\n", - " this.id = figure_id;\n", - "\n", - " this.ws = websocket;\n", - "\n", - " this.supports_binary = this.ws.binaryType !== undefined;\n", - "\n", - " if (!this.supports_binary) {\n", - " var warnings = document.getElementById('mpl-warnings');\n", - " if (warnings) {\n", - " warnings.style.display = 'block';\n", - " warnings.textContent =\n", - " 'This browser does not support binary websocket messages. ' +\n", - " 'Performance may be slow.';\n", - " }\n", - " }\n", - "\n", - " this.imageObj = new Image();\n", - "\n", - " this.context = undefined;\n", - " this.message = undefined;\n", - " this.canvas = undefined;\n", - " this.rubberband_canvas = undefined;\n", - " this.rubberband_context = undefined;\n", - " this.format_dropdown = undefined;\n", - "\n", - " this.image_mode = 'full';\n", - "\n", - " this.root = document.createElement('div');\n", - " this.root.setAttribute('style', 'display: inline-block');\n", - " this._root_extra_style(this.root);\n", - "\n", - " parent_element.appendChild(this.root);\n", - "\n", - " this._init_header(this);\n", - " this._init_canvas(this);\n", - " this._init_toolbar(this);\n", - "\n", - " var fig = this;\n", - "\n", - " this.waiting = false;\n", - "\n", - " this.ws.onopen = function () {\n", - " fig.send_message('supports_binary', { value: fig.supports_binary });\n", - " fig.send_message('send_image_mode', {});\n", - " if (fig.ratio !== 1) {\n", - " fig.send_message('set_device_pixel_ratio', {\n", - " device_pixel_ratio: fig.ratio,\n", - " });\n", - " }\n", - " fig.send_message('refresh', {});\n", - " };\n", - "\n", - " this.imageObj.onload = function () {\n", - " if (fig.image_mode === 'full') {\n", - " // Full images could contain transparency (where diff images\n", - " // almost always do), so we need to clear the canvas so that\n", - " // there is no ghosting.\n", - " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", - " }\n", - " fig.context.drawImage(fig.imageObj, 0, 0);\n", - " };\n", - "\n", - " this.imageObj.onunload = function () {\n", - " fig.ws.close();\n", - " };\n", - "\n", - " this.ws.onmessage = this._make_on_message_function(this);\n", - "\n", - " this.ondownload = ondownload;\n", - "};\n", - "\n", - "mpl.figure.prototype._init_header = function () {\n", - " var titlebar = document.createElement('div');\n", - " titlebar.classList =\n", - " 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix';\n", - " var titletext = document.createElement('div');\n", - " titletext.classList = 'ui-dialog-title';\n", - " titletext.setAttribute(\n", - " 'style',\n", - " 'width: 100%; text-align: center; padding: 3px;'\n", - " );\n", - " titlebar.appendChild(titletext);\n", - " this.root.appendChild(titlebar);\n", - " this.header = titletext;\n", - "};\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {};\n", - "\n", - "mpl.figure.prototype._root_extra_style = function (_canvas_div) {};\n", - "\n", - "mpl.figure.prototype._init_canvas = function () {\n", - " var fig = this;\n", - "\n", - " var canvas_div = (this.canvas_div = document.createElement('div'));\n", - " canvas_div.setAttribute(\n", - " 'style',\n", - " 'border: 1px solid #ddd;' +\n", - " 'box-sizing: content-box;' +\n", - " 'clear: both;' +\n", - " 'min-height: 1px;' +\n", - " 'min-width: 1px;' +\n", - " 'outline: 0;' +\n", - " 'overflow: hidden;' +\n", - " 'position: relative;' +\n", - " 'resize: both;'\n", - " );\n", - "\n", - " function on_keyboard_event_closure(name) {\n", - " return function (event) {\n", - " return fig.key_event(event, name);\n", - " };\n", - " }\n", - "\n", - " canvas_div.addEventListener(\n", - " 'keydown',\n", - " on_keyboard_event_closure('key_press')\n", - " );\n", - " canvas_div.addEventListener(\n", - " 'keyup',\n", - " on_keyboard_event_closure('key_release')\n", - " );\n", - "\n", - " this._canvas_extra_style(canvas_div);\n", - " this.root.appendChild(canvas_div);\n", - "\n", - " var canvas = (this.canvas = document.createElement('canvas'));\n", - " canvas.classList.add('mpl-canvas');\n", - " canvas.setAttribute('style', 'box-sizing: content-box;');\n", - "\n", - " this.context = canvas.getContext('2d');\n", - "\n", - " var backingStore =\n", - " this.context.backingStorePixelRatio ||\n", - " this.context.webkitBackingStorePixelRatio ||\n", - " this.context.mozBackingStorePixelRatio ||\n", - " this.context.msBackingStorePixelRatio ||\n", - " this.context.oBackingStorePixelRatio ||\n", - " this.context.backingStorePixelRatio ||\n", - " 1;\n", - "\n", - " this.ratio = (window.devicePixelRatio || 1) / backingStore;\n", - "\n", - " var rubberband_canvas = (this.rubberband_canvas = document.createElement(\n", - " 'canvas'\n", - " ));\n", - " rubberband_canvas.setAttribute(\n", - " 'style',\n", - " 'box-sizing: content-box; position: absolute; left: 0; top: 0; z-index: 1;'\n", - " );\n", - "\n", - " // Apply a ponyfill if ResizeObserver is not implemented by browser.\n", - " if (this.ResizeObserver === undefined) {\n", - " if (window.ResizeObserver !== undefined) {\n", - " this.ResizeObserver = window.ResizeObserver;\n", - " } else {\n", - " var obs = _JSXTOOLS_RESIZE_OBSERVER({});\n", - " this.ResizeObserver = obs.ResizeObserver;\n", - " }\n", - " }\n", - "\n", - " this.resizeObserverInstance = new this.ResizeObserver(function (entries) {\n", - " var nentries = entries.length;\n", - " for (var i = 0; i < nentries; i++) {\n", - " var entry = entries[i];\n", - " var width, height;\n", - " if (entry.contentBoxSize) {\n", - " if (entry.contentBoxSize instanceof Array) {\n", - " // Chrome 84 implements new version of spec.\n", - " width = entry.contentBoxSize[0].inlineSize;\n", - " height = entry.contentBoxSize[0].blockSize;\n", - " } else {\n", - " // Firefox implements old version of spec.\n", - " width = entry.contentBoxSize.inlineSize;\n", - " height = entry.contentBoxSize.blockSize;\n", - " }\n", - " } else {\n", - " // Chrome <84 implements even older version of spec.\n", - " width = entry.contentRect.width;\n", - " height = entry.contentRect.height;\n", - " }\n", - "\n", - " // Keep the size of the canvas and rubber band canvas in sync with\n", - " // the canvas container.\n", - " if (entry.devicePixelContentBoxSize) {\n", - " // Chrome 84 implements new version of spec.\n", - " canvas.setAttribute(\n", - " 'width',\n", - " entry.devicePixelContentBoxSize[0].inlineSize\n", - " );\n", - " canvas.setAttribute(\n", - " 'height',\n", - " entry.devicePixelContentBoxSize[0].blockSize\n", - " );\n", - " } else {\n", - " canvas.setAttribute('width', width * fig.ratio);\n", - " canvas.setAttribute('height', height * fig.ratio);\n", - " }\n", - " canvas.setAttribute(\n", - " 'style',\n", - " 'width: ' + width + 'px; height: ' + height + 'px;'\n", - " );\n", - "\n", - " rubberband_canvas.setAttribute('width', width);\n", - " rubberband_canvas.setAttribute('height', height);\n", - "\n", - " // And update the size in Python. We ignore the initial 0/0 size\n", - " // that occurs as the element is placed into the DOM, which should\n", - " // otherwise not happen due to the minimum size styling.\n", - " if (fig.ws.readyState == 1 && width != 0 && height != 0) {\n", - " fig.request_resize(width, height);\n", - " }\n", - " }\n", - " });\n", - " this.resizeObserverInstance.observe(canvas_div);\n", - "\n", - " function on_mouse_event_closure(name) {\n", - " return function (event) {\n", - " return fig.mouse_event(event, name);\n", - " };\n", - " }\n", - "\n", - " rubberband_canvas.addEventListener(\n", - " 'mousedown',\n", - " on_mouse_event_closure('button_press')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseup',\n", - " on_mouse_event_closure('button_release')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'dblclick',\n", - " on_mouse_event_closure('dblclick')\n", - " );\n", - " // Throttle sequential mouse events to 1 every 20ms.\n", - " rubberband_canvas.addEventListener(\n", - " 'mousemove',\n", - " on_mouse_event_closure('motion_notify')\n", - " );\n", - "\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseenter',\n", - " on_mouse_event_closure('figure_enter')\n", - " );\n", - " rubberband_canvas.addEventListener(\n", - " 'mouseleave',\n", - " on_mouse_event_closure('figure_leave')\n", - " );\n", - "\n", - " canvas_div.addEventListener('wheel', function (event) {\n", - " if (event.deltaY < 0) {\n", - " event.step = 1;\n", - " } else {\n", - " event.step = -1;\n", - " }\n", - " on_mouse_event_closure('scroll')(event);\n", - " });\n", - "\n", - " canvas_div.appendChild(canvas);\n", - " canvas_div.appendChild(rubberband_canvas);\n", - "\n", - " this.rubberband_context = rubberband_canvas.getContext('2d');\n", - " this.rubberband_context.strokeStyle = '#000000';\n", - "\n", - " this._resize_canvas = function (width, height, forward) {\n", - " if (forward) {\n", - " canvas_div.style.width = width + 'px';\n", - " canvas_div.style.height = height + 'px';\n", - " }\n", - " };\n", - "\n", - " // Disable right mouse context menu.\n", - " this.rubberband_canvas.addEventListener('contextmenu', function (_e) {\n", - " event.preventDefault();\n", - " return false;\n", - " });\n", - "\n", - " function set_focus() {\n", - " canvas.focus();\n", - " canvas_div.focus();\n", - " }\n", - "\n", - " window.setTimeout(set_focus, 100);\n", - "};\n", - "\n", - "mpl.figure.prototype._init_toolbar = function () {\n", - " var fig = this;\n", - "\n", - " var toolbar = document.createElement('div');\n", - " toolbar.classList = 'mpl-toolbar';\n", - " this.root.appendChild(toolbar);\n", - "\n", - " function on_click_closure(name) {\n", - " return function (_event) {\n", - " return fig.toolbar_button_onclick(name);\n", - " };\n", - " }\n", - "\n", - " function on_mouseover_closure(tooltip) {\n", - " return function (event) {\n", - " if (!event.currentTarget.disabled) {\n", - " return fig.toolbar_button_onmouseover(tooltip);\n", - " }\n", - " };\n", - " }\n", - "\n", - " fig.buttons = {};\n", - " var buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'mpl-button-group';\n", - " for (var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " /* Instead of a spacer, we start a new button group. */\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - " buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'mpl-button-group';\n", - " continue;\n", - " }\n", - "\n", - " var button = (fig.buttons[name] = document.createElement('button'));\n", - " button.classList = 'mpl-widget';\n", - " button.setAttribute('role', 'button');\n", - " button.setAttribute('aria-disabled', 'false');\n", - " button.addEventListener('click', on_click_closure(method_name));\n", - " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", - "\n", - " var icon_img = document.createElement('img');\n", - " icon_img.src = '_images/' + image + '.png';\n", - " icon_img.srcset = '_images/' + image + '_large.png 2x';\n", - " icon_img.alt = tooltip;\n", - " button.appendChild(icon_img);\n", - "\n", - " buttonGroup.appendChild(button);\n", - " }\n", - "\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - "\n", - " var fmt_picker = document.createElement('select');\n", - " fmt_picker.classList = 'mpl-widget';\n", - " toolbar.appendChild(fmt_picker);\n", - " this.format_dropdown = fmt_picker;\n", - "\n", - " for (var ind in mpl.extensions) {\n", - " var fmt = mpl.extensions[ind];\n", - " var option = document.createElement('option');\n", - " option.selected = fmt === mpl.default_extension;\n", - " option.innerHTML = fmt;\n", - " fmt_picker.appendChild(option);\n", - " }\n", - "\n", - " var status_bar = document.createElement('span');\n", - " status_bar.classList = 'mpl-message';\n", - " toolbar.appendChild(status_bar);\n", - " this.message = status_bar;\n", - "};\n", - "\n", - "mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) {\n", - " // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", - " // which will in turn request a refresh of the image.\n", - " this.send_message('resize', { width: x_pixels, height: y_pixels });\n", - "};\n", - "\n", - "mpl.figure.prototype.send_message = function (type, properties) {\n", - " properties['type'] = type;\n", - " properties['figure_id'] = this.id;\n", - " this.ws.send(JSON.stringify(properties));\n", - "};\n", - "\n", - "mpl.figure.prototype.send_draw_message = function () {\n", - " if (!this.waiting) {\n", - " this.waiting = true;\n", - " this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id }));\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", - " var format_dropdown = fig.format_dropdown;\n", - " var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", - " fig.ondownload(fig, format);\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_resize = function (fig, msg) {\n", - " var size = msg['size'];\n", - " if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) {\n", - " fig._resize_canvas(size[0], size[1], msg['forward']);\n", - " fig.send_message('refresh', {});\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_rubberband = function (fig, msg) {\n", - " var x0 = msg['x0'] / fig.ratio;\n", - " var y0 = (fig.canvas.height - msg['y0']) / fig.ratio;\n", - " var x1 = msg['x1'] / fig.ratio;\n", - " var y1 = (fig.canvas.height - msg['y1']) / fig.ratio;\n", - " x0 = Math.floor(x0) + 0.5;\n", - " y0 = Math.floor(y0) + 0.5;\n", - " x1 = Math.floor(x1) + 0.5;\n", - " y1 = Math.floor(y1) + 0.5;\n", - " var min_x = Math.min(x0, x1);\n", - " var min_y = Math.min(y0, y1);\n", - " var width = Math.abs(x1 - x0);\n", - " var height = Math.abs(y1 - y0);\n", - "\n", - " fig.rubberband_context.clearRect(\n", - " 0,\n", - " 0,\n", - " fig.canvas.width / fig.ratio,\n", - " fig.canvas.height / fig.ratio\n", - " );\n", - "\n", - " fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_figure_label = function (fig, msg) {\n", - " // Updates the figure title.\n", - " fig.header.textContent = msg['label'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_cursor = function (fig, msg) {\n", - " fig.rubberband_canvas.style.cursor = msg['cursor'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_message = function (fig, msg) {\n", - " fig.message.textContent = msg['message'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_draw = function (fig, _msg) {\n", - " // Request the server to send over a new figure.\n", - " fig.send_draw_message();\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_image_mode = function (fig, msg) {\n", - " fig.image_mode = msg['mode'];\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_history_buttons = function (fig, msg) {\n", - " for (var key in msg) {\n", - " if (!(key in fig.buttons)) {\n", - " continue;\n", - " }\n", - " fig.buttons[key].disabled = !msg[key];\n", - " fig.buttons[key].setAttribute('aria-disabled', !msg[key]);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_navigate_mode = function (fig, msg) {\n", - " if (msg['mode'] === 'PAN') {\n", - " fig.buttons['Pan'].classList.add('active');\n", - " fig.buttons['Zoom'].classList.remove('active');\n", - " } else if (msg['mode'] === 'ZOOM') {\n", - " fig.buttons['Pan'].classList.remove('active');\n", - " fig.buttons['Zoom'].classList.add('active');\n", - " } else {\n", - " fig.buttons['Pan'].classList.remove('active');\n", - " fig.buttons['Zoom'].classList.remove('active');\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.updated_canvas_event = function () {\n", - " // Called whenever the canvas gets updated.\n", - " this.send_message('ack', {});\n", - "};\n", - "\n", - "// A function to construct a web socket function for onmessage handling.\n", - "// Called in the figure constructor.\n", - "mpl.figure.prototype._make_on_message_function = function (fig) {\n", - " return function socket_on_message(evt) {\n", - " if (evt.data instanceof Blob) {\n", - " var img = evt.data;\n", - " if (img.type !== 'image/png') {\n", - " /* FIXME: We get \"Resource interpreted as Image but\n", - " * transferred with MIME type text/plain:\" errors on\n", - " * Chrome. But how to set the MIME type? It doesn't seem\n", - " * to be part of the websocket stream */\n", - " img.type = 'image/png';\n", - " }\n", - "\n", - " /* Free the memory for the previous frames */\n", - " if (fig.imageObj.src) {\n", - " (window.URL || window.webkitURL).revokeObjectURL(\n", - " fig.imageObj.src\n", - " );\n", - " }\n", - "\n", - " fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", - " img\n", - " );\n", - " fig.updated_canvas_event();\n", - " fig.waiting = false;\n", - " return;\n", - " } else if (\n", - " typeof evt.data === 'string' &&\n", - " evt.data.slice(0, 21) === 'data:image/png;base64'\n", - " ) {\n", - " fig.imageObj.src = evt.data;\n", - " fig.updated_canvas_event();\n", - " fig.waiting = false;\n", - " return;\n", - " }\n", - "\n", - " var msg = JSON.parse(evt.data);\n", - " var msg_type = msg['type'];\n", - "\n", - " // Call the \"handle_{type}\" callback, which takes\n", - " // the figure and JSON message as its only arguments.\n", - " try {\n", - " var callback = fig['handle_' + msg_type];\n", - " } catch (e) {\n", - " console.log(\n", - " \"No handler for the '\" + msg_type + \"' message type: \",\n", - " msg\n", - " );\n", - " return;\n", - " }\n", - "\n", - " if (callback) {\n", - " try {\n", - " // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", - " callback(fig, msg);\n", - " } catch (e) {\n", - " console.log(\n", - " \"Exception inside the 'handler_\" + msg_type + \"' callback:\",\n", - " e,\n", - " e.stack,\n", - " msg\n", - " );\n", - " }\n", - " }\n", - " };\n", - "};\n", - "\n", - "// from https://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", - "mpl.findpos = function (e) {\n", - " //this section is from http://www.quirksmode.org/js/events_properties.html\n", - " var targ;\n", - " if (!e) {\n", - " e = window.event;\n", - " }\n", - " if (e.target) {\n", - " targ = e.target;\n", - " } else if (e.srcElement) {\n", - " targ = e.srcElement;\n", - " }\n", - " if (targ.nodeType === 3) {\n", - " // defeat Safari bug\n", - " targ = targ.parentNode;\n", - " }\n", - "\n", - " // pageX,Y are the mouse positions relative to the document\n", - " var boundingRect = targ.getBoundingClientRect();\n", - " var x = e.pageX - (boundingRect.left + document.body.scrollLeft);\n", - " var y = e.pageY - (boundingRect.top + document.body.scrollTop);\n", - "\n", - " return { x: x, y: y };\n", - "};\n", - "\n", - "/*\n", - " * return a copy of an object with only non-object keys\n", - " * we need this to avoid circular references\n", - " * https://stackoverflow.com/a/24161582/3208463\n", - " */\n", - "function simpleKeys(original) {\n", - " return Object.keys(original).reduce(function (obj, key) {\n", - " if (typeof original[key] !== 'object') {\n", - " obj[key] = original[key];\n", - " }\n", - " return obj;\n", - " }, {});\n", - "}\n", - "\n", - "mpl.figure.prototype.mouse_event = function (event, name) {\n", - " var canvas_pos = mpl.findpos(event);\n", - "\n", - " if (name === 'button_press') {\n", - " this.canvas.focus();\n", - " this.canvas_div.focus();\n", - " }\n", - "\n", - " var x = canvas_pos.x * this.ratio;\n", - " var y = canvas_pos.y * this.ratio;\n", - "\n", - " this.send_message(name, {\n", - " x: x,\n", - " y: y,\n", - " button: event.button,\n", - " step: event.step,\n", - " guiEvent: simpleKeys(event),\n", - " });\n", - "\n", - " /* This prevents the web browser from automatically changing to\n", - " * the text insertion cursor when the button is pressed. We want\n", - " * to control all of the cursor setting manually through the\n", - " * 'cursor' event from matplotlib */\n", - " event.preventDefault();\n", - " return false;\n", - "};\n", - "\n", - "mpl.figure.prototype._key_event_extra = function (_event, _name) {\n", - " // Handle any extra behaviour associated with a key event\n", - "};\n", - "\n", - "mpl.figure.prototype.key_event = function (event, name) {\n", - " // Prevent repeat events\n", - " if (name === 'key_press') {\n", - " if (event.key === this._key) {\n", - " return;\n", - " } else {\n", - " this._key = event.key;\n", - " }\n", - " }\n", - " if (name === 'key_release') {\n", - " this._key = null;\n", - " }\n", - "\n", - " var value = '';\n", - " if (event.ctrlKey && event.key !== 'Control') {\n", - " value += 'ctrl+';\n", - " }\n", - " else if (event.altKey && event.key !== 'Alt') {\n", - " value += 'alt+';\n", - " }\n", - " else if (event.shiftKey && event.key !== 'Shift') {\n", - " value += 'shift+';\n", - " }\n", - "\n", - " value += 'k' + event.key;\n", - "\n", - " this._key_event_extra(event, name);\n", - "\n", - " this.send_message(name, { key: value, guiEvent: simpleKeys(event) });\n", - " return false;\n", - "};\n", - "\n", - "mpl.figure.prototype.toolbar_button_onclick = function (name) {\n", - " if (name === 'download') {\n", - " this.handle_save(this, null);\n", - " } else {\n", - " this.send_message('toolbar_button', { name: name });\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) {\n", - " this.message.textContent = tooltip;\n", - "};\n", - "\n", - "///////////////// REMAINING CONTENT GENERATED BY embed_js.py /////////////////\n", - "// prettier-ignore\n", - "var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError(\"Constructor requires 'new' operator\");i.set(this,e)}function h(){throw new TypeError(\"Function is not a constructor\")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line\n", - "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Left button pans, Right button zooms\\nx/y fixes axis, CTRL fixes aspect\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\\nx/y fixes axis\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", - "\n", - "mpl.extensions = [\"eps\", \"jpeg\", \"pgf\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n", - "\n", - "mpl.default_extension = \"png\";/* global mpl */\n", - "\n", - "var comm_websocket_adapter = function (comm) {\n", - " // Create a \"websocket\"-like object which calls the given IPython comm\n", - " // object with the appropriate methods. Currently this is a non binary\n", - " // socket, so there is still some room for performance tuning.\n", - " var ws = {};\n", - "\n", - " ws.binaryType = comm.kernel.ws.binaryType;\n", - " ws.readyState = comm.kernel.ws.readyState;\n", - " function updateReadyState(_event) {\n", - " if (comm.kernel.ws) {\n", - " ws.readyState = comm.kernel.ws.readyState;\n", - " } else {\n", - " ws.readyState = 3; // Closed state.\n", - " }\n", - " }\n", - " comm.kernel.ws.addEventListener('open', updateReadyState);\n", - " comm.kernel.ws.addEventListener('close', updateReadyState);\n", - " comm.kernel.ws.addEventListener('error', updateReadyState);\n", - "\n", - " ws.close = function () {\n", - " comm.close();\n", - " };\n", - " ws.send = function (m) {\n", - " //console.log('sending', m);\n", - " comm.send(m);\n", - " };\n", - " // Register the callback with on_msg.\n", - " comm.on_msg(function (msg) {\n", - " //console.log('receiving', msg['content']['data'], msg);\n", - " var data = msg['content']['data'];\n", - " if (data['blob'] !== undefined) {\n", - " data = {\n", - " data: new Blob(msg['buffers'], { type: data['blob'] }),\n", - " };\n", - " }\n", - " // Pass the mpl event to the overridden (by mpl) onmessage function.\n", - " ws.onmessage(data);\n", - " });\n", - " return ws;\n", - "};\n", - "\n", - "mpl.mpl_figure_comm = function (comm, msg) {\n", - " // This is the function which gets called when the mpl process\n", - " // starts-up an IPython Comm through the \"matplotlib\" channel.\n", - "\n", - " var id = msg.content.data.id;\n", - " // Get hold of the div created by the display call when the Comm\n", - " // socket was opened in Python.\n", - " var element = document.getElementById(id);\n", - " var ws_proxy = comm_websocket_adapter(comm);\n", - "\n", - " function ondownload(figure, _format) {\n", - " window.open(figure.canvas.toDataURL());\n", - " }\n", - "\n", - " var fig = new mpl.figure(id, ws_proxy, ondownload, element);\n", - "\n", - " // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", - " // web socket which is closed, not our websocket->open comm proxy.\n", - " ws_proxy.onopen();\n", - "\n", - " fig.parent_element = element;\n", - " fig.cell_info = mpl.find_output_cell(\"
\");\n", - " if (!fig.cell_info) {\n", - " console.error('Failed to find cell for figure', id, fig);\n", - " return;\n", - " }\n", - " fig.cell_info[0].output_area.element.on(\n", - " 'cleared',\n", - " { fig: fig },\n", - " fig._remove_fig_handler\n", - " );\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_close = function (fig, msg) {\n", - " var width = fig.canvas.width / fig.ratio;\n", - " fig.cell_info[0].output_area.element.off(\n", - " 'cleared',\n", - " fig._remove_fig_handler\n", - " );\n", - " fig.resizeObserverInstance.unobserve(fig.canvas_div);\n", - "\n", - " // Update the output cell to use the data from the current canvas.\n", - " fig.push_to_output();\n", - " var dataURL = fig.canvas.toDataURL();\n", - " // Re-enable the keyboard manager in IPython - without this line, in FF,\n", - " // the notebook keyboard shortcuts fail.\n", - " IPython.keyboard_manager.enable();\n", - " fig.parent_element.innerHTML =\n", - " '';\n", - " fig.close_ws(fig, msg);\n", - "};\n", - "\n", - "mpl.figure.prototype.close_ws = function (fig, msg) {\n", - " fig.send_message('closing', msg);\n", - " // fig.ws.close()\n", - "};\n", - "\n", - "mpl.figure.prototype.push_to_output = function (_remove_interactive) {\n", - " // Turn the data on the canvas into data in the output cell.\n", - " var width = this.canvas.width / this.ratio;\n", - " var dataURL = this.canvas.toDataURL();\n", - " this.cell_info[1]['text/html'] =\n", - " '';\n", - "};\n", - "\n", - "mpl.figure.prototype.updated_canvas_event = function () {\n", - " // Tell IPython that the notebook contents must change.\n", - " IPython.notebook.set_dirty(true);\n", - " this.send_message('ack', {});\n", - " var fig = this;\n", - " // Wait a second, then push the new image to the DOM so\n", - " // that it is saved nicely (might be nice to debounce this).\n", - " setTimeout(function () {\n", - " fig.push_to_output();\n", - " }, 1000);\n", - "};\n", - "\n", - "mpl.figure.prototype._init_toolbar = function () {\n", - " var fig = this;\n", - "\n", - " var toolbar = document.createElement('div');\n", - " toolbar.classList = 'btn-toolbar';\n", - " this.root.appendChild(toolbar);\n", - "\n", - " function on_click_closure(name) {\n", - " return function (_event) {\n", - " return fig.toolbar_button_onclick(name);\n", - " };\n", - " }\n", - "\n", - " function on_mouseover_closure(tooltip) {\n", - " return function (event) {\n", - " if (!event.currentTarget.disabled) {\n", - " return fig.toolbar_button_onmouseover(tooltip);\n", - " }\n", - " };\n", - " }\n", - "\n", - " fig.buttons = {};\n", - " var buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'btn-group';\n", - " var button;\n", - " for (var toolbar_ind in mpl.toolbar_items) {\n", - " var name = mpl.toolbar_items[toolbar_ind][0];\n", - " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", - " var image = mpl.toolbar_items[toolbar_ind][2];\n", - " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", - "\n", - " if (!name) {\n", - " /* Instead of a spacer, we start a new button group. */\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - " buttonGroup = document.createElement('div');\n", - " buttonGroup.classList = 'btn-group';\n", - " continue;\n", - " }\n", - "\n", - " button = fig.buttons[name] = document.createElement('button');\n", - " button.classList = 'btn btn-default';\n", - " button.href = '#';\n", - " button.title = name;\n", - " button.innerHTML = '';\n", - " button.addEventListener('click', on_click_closure(method_name));\n", - " button.addEventListener('mouseover', on_mouseover_closure(tooltip));\n", - " buttonGroup.appendChild(button);\n", - " }\n", - "\n", - " if (buttonGroup.hasChildNodes()) {\n", - " toolbar.appendChild(buttonGroup);\n", - " }\n", - "\n", - " // Add the status bar.\n", - " var status_bar = document.createElement('span');\n", - " status_bar.classList = 'mpl-message pull-right';\n", - " toolbar.appendChild(status_bar);\n", - " this.message = status_bar;\n", - "\n", - " // Add the close button to the window.\n", - " var buttongrp = document.createElement('div');\n", - " buttongrp.classList = 'btn-group inline pull-right';\n", - " button = document.createElement('button');\n", - " button.classList = 'btn btn-mini btn-primary';\n", - " button.href = '#';\n", - " button.title = 'Stop Interaction';\n", - " button.innerHTML = '';\n", - " button.addEventListener('click', function (_evt) {\n", - " fig.handle_close(fig, {});\n", - " });\n", - " button.addEventListener(\n", - " 'mouseover',\n", - " on_mouseover_closure('Stop Interaction')\n", - " );\n", - " buttongrp.appendChild(button);\n", - " var titlebar = this.root.querySelector('.ui-dialog-titlebar');\n", - " titlebar.insertBefore(buttongrp, titlebar.firstChild);\n", - "};\n", - "\n", - "mpl.figure.prototype._remove_fig_handler = function (event) {\n", - " var fig = event.data.fig;\n", - " if (event.target !== this) {\n", - " // Ignore bubbled events from children.\n", - " return;\n", - " }\n", - " fig.close_ws(fig, {});\n", - "};\n", - "\n", - "mpl.figure.prototype._root_extra_style = function (el) {\n", - " el.style.boxSizing = 'content-box'; // override notebook setting of border-box.\n", - "};\n", - "\n", - "mpl.figure.prototype._canvas_extra_style = function (el) {\n", - " // this is important to make the div 'focusable\n", - " el.setAttribute('tabindex', 0);\n", - " // reach out to IPython and tell the keyboard manager to turn it's self\n", - " // off when our div gets focus\n", - "\n", - " // location in version 3\n", - " if (IPython.notebook.keyboard_manager) {\n", - " IPython.notebook.keyboard_manager.register_events(el);\n", - " } else {\n", - " // location in version 2\n", - " IPython.keyboard_manager.register_events(el);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype._key_event_extra = function (event, _name) {\n", - " // Check for shift+enter\n", - " if (event.shiftKey && event.which === 13) {\n", - " this.canvas_div.blur();\n", - " // select the cell after this one\n", - " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", - " IPython.notebook.select(index + 1);\n", - " }\n", - "};\n", - "\n", - "mpl.figure.prototype.handle_save = function (fig, _msg) {\n", - " fig.ondownload(fig, null);\n", - "};\n", - "\n", - "mpl.find_output_cell = function (html_output) {\n", - " // Return the cell and output element which can be found *uniquely* in the notebook.\n", - " // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", - " // IPython event is triggered only after the cells have been serialised, which for\n", - " // our purposes (turning an active figure into a static one), is too late.\n", - " var cells = IPython.notebook.get_cells();\n", - " var ncells = cells.length;\n", - " for (var i = 0; i < ncells; i++) {\n", - " var cell = cells[i];\n", - " if (cell.cell_type === 'code') {\n", - " for (var j = 0; j < cell.output_area.outputs.length; j++) {\n", - " var data = cell.output_area.outputs[j];\n", - " if (data.data) {\n", - " // IPython >= 3 moved mimebundle to data attribute of output\n", - " data = data.data;\n", - " }\n", - " if (data['text/html'] === html_output) {\n", - " return [cell, data, j];\n", - " }\n", - " }\n", - " }\n", - " }\n", - "};\n", - "\n", - "// Register the function which deals with the matplotlib target/channel.\n", - "// The kernel may be null if the page has been refreshed.\n", - "if (IPython.notebook.kernel !== null) {\n", - " IPython.notebook.kernel.comm_manager.register_target(\n", - " 'matplotlib',\n", - " mpl.mpl_figure_comm\n", - " );\n", - "}\n" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, { "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "da30fd6221fe4677b48a6d6c5295cadb", + "version_major": 2, + "version_minor": 0 + }, + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAyAAAAPoCAYAAAAmy5qxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAA9hAAAPYQGoP6dpAADSgklEQVR4nOzdeVyU9fo//tcwM2zGIqIiiWLqEUEFN8wlsSPu6zmm/jTNcsElbbG0zbSOdkyPZh01tw9u38rllI5IWqLHXFECAzc07YBogGgii2wD8/79ccfALagsszHzej4e88i55pp7rnuuYeLi3hRCCAEiIiIiIiITsDN3AUREREREZDs4gBARERERkclwACEiIiIiIpPhAEJERERERCbDAYSIiIiIiEyGAwgREREREZkMBxAiIiIiIjIZDiBERERERGQyHECIiIiIiMhkOIAQEREREZHJcAAhIiIiIiKT4QBCREREREQmwwGEiIiIiIhMhgMIERERERGZDAcQIiIiIiIyGQ4gRERERERkMhxAiIiIiIjIZDiAEBERERGRyXAAISIiIiIik+EAQkREREREJsMBhIiIiIiITIYDCBERERERmQwHECIiIiIiMhkOIEREREREZDIcQIiIiIiIyGQ4gBARERERkclwACEiIiIiIpPhAEJERERERCbDAYSIiIiIiEyGAwgREREREZkMBxAiIiIiIjIZDiBERERERGQyHECIiIiIiMhkOIAQEREREZHJcAAhIiIiIiKT4QBCREREREQmwwGEiIiIiIhMhgMIERERERGZDAcQIiIiIiIyGQ4gRERERERkMhxAiIiIiIjIZDiAEBERERGRyXAAISIiIiIik+EAQkREREREJsMBhIiIiIiITIYDCBERERERmQwHECIiIiIiMhkOIEREREREZDIcQIiIiIiIyGQ4gBARERERkclwACEiIiIiIpPhAEJERERERCbDAYSIiIiIiEyGAwgREREREZkMBxAiIiIiIjIZDiBERERERGQyHECIiIiIiMhkOIAQEREREZHJcAAhIiIiIiKT4QBCREREREQmwwGEiIiIiIhMhgMIERERERGZDAcQIiIiIiIyGQ4gRERERERkMhxAiIiIiIjIZDiAEBERERGRyXAAISIiIiIik+EAQkREREREJsMBhIiIiIiITIYDCBERERERmQwHECIiIiIiMhkO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0iXTFG6BZs+ejQsXLgCQDi53cXHBxIkTERISgsLCQnh4eODixYsApCHu8OHD+gGksrNf7d+/H99//32lrxUcHAwhRLXOuEVEREREpsVdsB6i0+nwxhtvoGfPnmjXrp0+Pn78eDRv3hze3t44f/483nnnHVy9ehV79uwBAKSnp8uGDwD6++np6ZW+VmFhIQoLC/X3s7OzAQBarRZarVaWq9VqIYSATqeDTqer/YqaweLFi3H27Fk4Ozuje/fuGDVqFO7evYshQ4agY8eOCAgIQPPmzeHm5gadToeePXti7NixFr++Op0OQghotVoolcoqP6+0xw/3mqwT+21b2G/bwn7bFva59hSCV2+TmTlzJg4ePIiTJ0+iadOmj8z773//i759++L69eto2bIlwsLCcOPGDfz444/6nLy8PNSrVw8HDhzAoEGDKizjo48+wscff1wh/s0338iOLwGkU816eXnBx8cH9vb2tVhDMrSioiLcvHkT6enpKC42zMH/REREZJny8vIwfvx4ZGVlyXbHp6rjFpByZs+ejcjISBw/fvyxwwcAdOvWDQD0A4iXlxdiYmJkObdv3wYAeHl5VbqM9957D3PnztXfz87Oho+PD55//nk0aNBAlltQUICbN2/iqaeekp2SlsyvoKAATk5O6N27d7V6o9VqERUVhX79+kGtVhuxQrIE7LdtYb9tC/ttW0r3WKGa4wAC6RoSc+bMwd69e/HTTz+hRYsWT3xOfHw8AKBJkyYAgO7du+OTTz5BRkYGGjVqBACIioqCq6sr/P39K12Gg4MDHBwcKsTVanWFL7CSkhIoFArY2dnpr5FBlsHOzg4KhaLSvlVFTZ9HdRP7bVvYb9vCftsG9rj2OIAAePXVV/HNN99g3759cHFx0R+z4ebmBicnJ/z222/45ptvMHjwYDRo0ADnz5/Hm2++id69e+sPeO7fvz/8/f0xceJELF++HOnp6ViwYAFeffXVSocMIiIiIiJbxD+lA1i3bh2ysrLQp08fNGnSRH/btWsXAMDe3h6HDx9G//794efnh7feegujRo3C/v379ctQKpWIjIyEUqlE9+7dMWHCBLz00kuy64YQEREREdk6bgGBtAvW4/j4+FS4CnplmjdvjgMHDhiqLCIiIiIiq8MtIEREREREZDIcQIiIiIiIyGQ4gBARERERkclwACEiIiIiIpPh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N/fX8fHxatmypbZs2SJJuv/++3Xfffdp2bJlysrK0s0336z27dv7vHYAAABUDwPIRWbUNZfp1yNlXt0DMuoa3+9FOHz4sPr06aM+ffro9ddfV4MGDVRYWKg+ffqorKys2tu566671KdPH3344YdatmyZJk2apOnTp+v//t//68PqAQAAUF0MIBeZzk3i9dY93f1dRo1t3bpVv/zyiyZPnqy0tDRJ0ldffVVp7qpVq9S4cWNJ0q+//qrt27erdevW7tvT0tJ077336t5779Wjjz6ql156iQEEAAAgQHASuqRJkybpqquuUr169dSwYUPdeOON2rZtm0dOz549FRIS4nG59957PXIKCwvVv39/RUdHq2HDhho3bpxOnP4ppahU48aNZbPZlJ2dre+//17vvfeennnmmUpzn376aa1YsUIbN27UyJEjlZiYqBtvvFGS9OCDD+rjjz9WQUGB1q1bp7y8PI/hBAAAAP7FACLp008/1ejRo7Vq1Srl5ubK4XCod+/eOnz4sEfeqFGjVFRU5L5MnTrVfVt5ebn69++vsrIyffnll1qwYIHmz5+vCRMmWL2cWqlBgwaaP3++3n77bWVkZGjy5MmaNm1apbmTJ0/WAw88oE6dOqm4uFjvv/++bDabJFcfRo8erdatW6tv375q0aKFnn/+eSuXAgAAgCpwCJakpUuXelyfP3++GjZsqLVr16pHjx7ueHR0tJKTkyvdxrJly7R582YtX75cSUlJ6tixo5555hk98sgj+tvf/ub+BTkYffLJJ9XKGzp0qIYOHeoRM8a4v+7Zs6f7+g033FDpNrKzs8+vSAAAAFiCAaQSpaWlks78BO/XX39dr732mpKTkzVgwAA9+eSTio6OliTl5+erXbt2SkpKcuf36dNH9913nzZt2qQrrrjijO9z/PhxHT9+3H3dbrdLcn0eRsXnWlRwOBwyxsjpdLrfHQqBy+l0yhgjh8OhsLCwSnMqenx6r3Fxot/BhX4HF/odXOjzhWMAOY3T6dSDDz6oq6++Wm3btnXHb7vtNqWnpyslJUUbNmzQI488om3btmnRokWSpOLiYo/hQ5L7enFxcaXfa9KkSXrqqafOiOfl5bkHmwrh4eFKTk7WoUOHavSuUIHiyy+/1ODBg896+08//WRhNb5XVlamo0eP6rPPPjvneUC5ubkWVYVAQL+DC/0OLvQ7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STlj7wlwsWMPiI8kJiYqLCxMJSUlHvGSkhIlJydXep/k5OQq8yv+rck2YQ1f9LtCxfCxa9cu5ebmsvcjAPii359//rn27t2rxo0bKzw8XOHh4dq1a5cefvhhNWnSxCfrQPX4ot+JiYkKDw9XRkaGR07r1q15Fyw/80W/jx49qscee0wzZszQgAED1L59e40ZM0ZDhgzRtGnTfLMQIIAxgPiIzWZTp06dtGLFCnfM6XRqxYoV6tatW6X36datm0e+JOXm5rrzmzZtquTkZI8cu92u1atXn3WbsIYv+i2dHD527Nih5cuXKyEhwTcLQI34ot/Dhg3Thg0btH79evclJSVF48aN08cff+y7xeCcfNFvm82mq666Stu2bfPI2b59u9LT0728AtSEL/rtcDjkcDgUGur5a1dYWJh7bxgQVPx9FvzF7M033zSRkZFm/vz5ZvPmzebuu+82cXFxpri42BhjzLBhw8z48ePd+StXrjTh4eFm2rRpZsuWLWbixImVvg1vXFycWbx4sdmwYYMZNGgQb8MbILzd77KyMjNw4ECTmppq1q9fb4qKityX48eP+2WNOMkXz+/T8S5YgcMX/V60aJGJiIgwc+fONTt27DDZ2dkmLCzMfP7555avD5580e/MzEzTpk0bk5eXZ77//nvz6quvmqioKPP8889bvj7A3xhAfCw7O9s0btzY2Gw206VLF7Nq1Sr3bZmZmWbEiBEe+W+99ZZp0aKFsdlspk2bNubDDz/0uN3pdJonn3zSJCUlmcjISHPttdeabdu2WbEUVIM3+11QUGAkVXrJy8uzaEWoiref36djAAksvuj3K6+8Ypo3b26ioqJMhw4dTE5Ojq+XgWrydr+LiorMyJEjTUpKiomKijItW7Y006dPN06n04rlAAElxBhj/LkHBgAAAEDw4BwQAAAAAJZhAAEAAABgGQYQAAAAAJZhAAEAAABgGQYQAAAAAJZhAAEAAABgGQYQAAAAAJZhAAEAAABgGQYQAAAAAJZhAAEAXLCePXvqwQcfPCO+a9cu1alTR4cOHbK+KABAQGIAAQD4zOLFi9WrVy9dcskl/i4FABAgGEAAABdk5MiR+vTTTzVr1iyFhIQoJCREP/zwgyTXADJw4EBJ0ieffKIuXbqobt26iouL09VXX61du3b5sXIAgD+EGGOMv4sAANRepaWluv7669W2bVs9/fTTkqQGDRro4MGDSkpKUkFBgRo2bKjExESNGjVK9957r8rKyrRmzRr16tVLjRs39vMKAABWCvd3AQCA2i02NlY2m03R0dFKTk52x5csWaL27dsrJSVF+/fvV2lpqW644QY1a9ZMktS6dWt/lQwA8CMOwQIA+MSph1/Fx8dr5MiR6tOnjwYMGKBZs2apqKjIzxUCAPyBAQQA4HVlZWVaunSpewCRpFdffVX5+fnq3r27Fi5cqBYtWmjVqlV+rBIA4A8MIACAC2az2VReXu6+/sknn6h+/frq0KGDR94VV1yhRx99VF9++aXatm2rN954w+pSAQB+xgACALhgTZo00erVq/XDDz/o559/Vk5Ojsfej4KCAj366KPKz8/Xrl27tGzZMu3YsYPzQAAgCHESOgDggv3lL3/RiBEjlJGRoaNHjyotLU3z5s1z3x4dHa2tW7dqwYIF+uWXX9SoUSONHj1a99xzjx+rBgD4A2/DCwDwqnXr1ukPf/iD9u3bp4iICH+XAwAIMByCBQDwqhMnTig7O5vhAwBQKfaAAAAAALAMe0AAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBlGEAAAAAAWIYBBAAAAIBl/j/rZNQSxW5hGgAAAABJRU5ErkJggg==", "text/html": [ - "" + "\n", + "
\n", + "
\n", + " Figure\n", + "
\n", + " \n", + "
\n", + " " ], "text/plain": [ - "" + "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" ] }, "metadata": {}, @@ -2761,16 +947,16 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "C:\\Users\\arnet\\AppData\\Roaming\\Python\\Python39\\site-packages\\numpy\\core\\fromnumeric.py:3419: RuntimeWarning: Mean of empty slice.\n", + "C:\\Users\\arnet\\anaconda3\\envs\\gem_dev\\lib\\site-packages\\numpy\\core\\fromnumeric.py:3372: RuntimeWarning: Mean of empty slice.\n", " return _methods._mean(a, axis=axis, dtype=dtype,\n", - "C:\\Users\\arnet\\AppData\\Roaming\\Python\\Python39\\site-packages\\numpy\\core\\_methods.py:188: RuntimeWarning: invalid value encountered in double_scalars\n", + "C:\\Users\\arnet\\anaconda3\\envs\\gem_dev\\lib\\site-packages\\numpy\\core\\_methods.py:170: RuntimeWarning: invalid value encountered in double_scalars\n", " ret = ret.dtype.type(ret / rcount)\n" ] } From 49b81e9c67be977c3d767fdec5568500e6235e37 Mon Sep 17 00:00:00 2001 From: Arne Date: Fri, 25 Feb 2022 18:43:17 +0100 Subject: [PATCH 51/79] bugfix --- .../ddpg_pmsm_dq_current_control.py | 8 ++++++-- .../state_action_processors/dq_to_abc_action_processor.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py b/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py index 23a8aec4..fe310e08 100644 --- a/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py +++ b/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py @@ -97,7 +97,11 @@ def reset(self, **kwargs): # Change the motor nominal values nominal_values = {key: 0.7 * limit for key, limit in limit_values.items()} - state_action_processors = (DeadTimeProcessor(), DqToAbcActionProcessor.make('PMSM')) + state_action_processors = ( + DeadTimeProcessor(), + DqToAbcActionProcessor.make('PMSM'), + ) + # Create the environment env = gem.make( # Choose the permanent magnet synchronous motor with continuous-control-set @@ -114,7 +118,7 @@ def reset(self, **kwargs): load=ConstantSpeedLoad(omega_fixed=1000 * np.pi / 30), # Define which numerical solver is to be used for the simulation - ode_solver='scipy.solve_ivp', + ode_solver='scipy.ode', # Pass the previously defined reference generator reference_generator=rg, diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py index f7bc7a47..68a8fbbe 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py @@ -21,7 +21,7 @@ def _transformation(action, angle): Returns: numpy.ndarray[float]: The action in the abc-space """ - return ps.ThreePhaseMotor.t_32(ps.ThreePhaseMotor.q_inv(action, angle)) + return ps.ThreePhaseMotor.t_32(ps.ThreePhaseMotor.q(action, angle)) _registry = {} From be7e6ea15fe89478311d10063b839912b0bb026c Mon Sep 17 00:00:00 2001 From: Arne Date: Fri, 25 Feb 2022 18:48:49 +0100 Subject: [PATCH 52/79] testfix --- .../test_dq_to_abc_action_processor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_state_action_processors/test_dq_to_abc_action_processor.py b/tests/test_state_action_processors/test_dq_to_abc_action_processor.py index 7e245ce0..5fe26fc6 100644 --- a/tests/test_state_action_processors/test_dq_to_abc_action_processor.py +++ b/tests/test_state_action_processors/test_dq_to_abc_action_processor.py @@ -27,8 +27,8 @@ def test_action_space(self, processor, physical_system): ['dq_action', 'state', 'abc_action'], [ (np.array([0.0, .0]), np.array([0.0, 0.0, 0.0]), np.array([0., 0., 0.])), - (np.array([0.0, 1.0]), np.array([12.8, 0.123, 0.0]), np.array([0.23752263, 0.72248018, -0.96000281])), - (np.array([0.0, .5]), np.array([-10.0, 0.123, 0.0]), np.array([0.49324109, -0.17566335, -0.31757774])), + (np.array([0.0, 1.0]), np.array([12.8, 0.123, 0.0]), np.array([-0.23752263, 0.96000281, -0.72248018])), + (np.array([0.0, .5]), np.array([-10.0, 0.123, 0.0]), np.array([-0.49324109, 0.31757774, 0.17566335])), ] ) def test_simulate(self, reset_processor, physical_system, dq_action, state, abc_action): From 2e3a6ec7b32e6912486d0416d0299dbea10bb7d3 Mon Sep 17 00:00:00 2001 From: Arne Date: Fri, 25 Feb 2022 19:11:02 +0100 Subject: [PATCH 53/79] Converter tau fix --- .../physical_systems/converters.py | 29 +++++++++++++++++++ .../physical_systems/physical_systems.py | 2 ++ 2 files changed, 31 insertions(+) diff --git a/gym_electric_motor/physical_systems/converters.py b/gym_electric_motor/physical_systems/converters.py index 04dbb2bd..8811d0d1 100644 --- a/gym_electric_motor/physical_systems/converters.py +++ b/gym_electric_motor/physical_systems/converters.py @@ -25,6 +25,15 @@ class PowerElectronicConverter: #: Default action that is taken after a reset. _reset_action = None + @property + def tau(self): + """Float: Time of one simulation step in seconds.""" + return self._tau + + @tau.setter + def tau(self, value): + self._tau = float(value) + def __init__(self, tau, dead_time=False, interlocking_time=0.0): """ :param tau: Discrete time step of the system in seconds @@ -493,6 +502,16 @@ class FiniteMultiConverter(FiniteConverter): [subconverter[0].voltages.high, subconverter[1].voltages.high, ...]) """ + @property + def tau(self): + return super()._tau + + @tau.setter + def tau(self, value): + self._tau = float(value) + for sub_converter in self._subconverters: + sub_converter.tau = value + @property def subconverters(self): return self._subconverters @@ -598,6 +617,16 @@ class ContMultiConverter(ContDynamicallyAveragedConverter): [subconverter[0].voltages.high, subconverter[1].voltages.high, ...]) """ + @property + def tau(self): + return super()._tau + + @tau.setter + def tau(self, value): + self._tau = float(value) + for sub_converter in self._subconverters: + sub_converter.tau = value + def __init__(self, subconverters, **kwargs): """ Args: diff --git a/gym_electric_motor/physical_systems/physical_systems.py b/gym_electric_motor/physical_systems/physical_systems.py index 9eed4df0..a92d116f 100644 --- a/gym_electric_motor/physical_systems/physical_systems.py +++ b/gym_electric_motor/physical_systems/physical_systems.py @@ -68,6 +68,7 @@ def __init__(self, converter, motor, load, supply, ode_solver, noise_generator=N self._mechanical_load = load self._supply = supply self._noise_generator = noise_generator + state_names = self._build_state_names() self._noise_generator.set_state_names(state_names) self._ode_solver = ode_solver @@ -99,6 +100,7 @@ def __init__(self, converter, motor, load, supply, ode_solver, noise_generator=N self._supply, self._converter, self._electrical_motor, self._mechanical_load, self._ode_solver, self._noise_generator ] + self._converter.tau = self.tau def _set_limits(self): """ From 0ae1450c915a484a4ac0f97f89ac7300df9dbb79 Mon Sep 17 00:00:00 2001 From: Arne Date: Fri, 25 Feb 2022 19:12:19 +0100 Subject: [PATCH 54/79] tau converter fix --- gym_electric_motor/physical_systems/converters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gym_electric_motor/physical_systems/converters.py b/gym_electric_motor/physical_systems/converters.py index 8811d0d1..6549797a 100644 --- a/gym_electric_motor/physical_systems/converters.py +++ b/gym_electric_motor/physical_systems/converters.py @@ -504,7 +504,7 @@ class FiniteMultiConverter(FiniteConverter): @property def tau(self): - return super()._tau + return self._tau @tau.setter def tau(self, value): @@ -619,7 +619,7 @@ class ContMultiConverter(ContDynamicallyAveragedConverter): @property def tau(self): - return super()._tau + return self._tau @tau.setter def tau(self, value): From 57f097f124cc36bf394cee8f2681cde5c11af9e7 Mon Sep 17 00:00:00 2001 From: Arne Date: Tue, 15 Mar 2022 20:57:26 +0100 Subject: [PATCH 55/79] Fixed bug due to merging --- gym_electric_motor/physical_systems/converters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gym_electric_motor/physical_systems/converters.py b/gym_electric_motor/physical_systems/converters.py index 5923b8d5..23d44a3e 100644 --- a/gym_electric_motor/physical_systems/converters.py +++ b/gym_electric_motor/physical_systems/converters.py @@ -501,7 +501,7 @@ def tau(self): @tau.setter def tau(self, value): self._tau = float(value) - for sub_converter in self._subconverters: + for sub_converter in self._sub_converters: sub_converter.tau = value def sub_converters(self): @@ -616,7 +616,7 @@ def tau(self): @tau.setter def tau(self, value): self._tau = float(value) - for sub_converter in self._subconverters: + for sub_converter in self._sub_converters: sub_converter.tau = value def __init__(self, subconverters, **kwargs): From bffd6a78b577ec1a8168a5fcd4743ec777527a3d Mon Sep 17 00:00:00 2001 From: Arne Date: Tue, 22 Mar 2022 19:56:02 +0100 Subject: [PATCH 56/79] TwoVoltageQuadrantConverter and comment fixes --- .../environments/eesm/finite_sc_eesm.rst | 2 +- .../envs/gym_eesm/cont_cc_eesm_env.py | 16 ++-- .../envs/gym_eesm/cont_sc_eesm_env.py | 10 +-- .../envs/gym_eesm/cont_tc_eesm_env.py | 10 +-- .../envs/gym_eesm/finite_cc_eesm_env.py | 10 +-- .../envs/gym_eesm/finite_sc_eesm_env.py | 12 +-- .../envs/gym_eesm/finite_tc_eesm_env.py | 12 +-- .../physical_systems/__init__.py | 11 ++- .../physical_systems/converters.py | 88 ++++++++++++++++--- .../test_physical_systems/test_converters.py | 6 +- 10 files changed, 123 insertions(+), 54 deletions(-) diff --git a/docs/parts/environments/eesm/finite_sc_eesm.rst b/docs/parts/environments/eesm/finite_sc_eesm.rst index c66cfd4a..52cb5585 100644 --- a/docs/parts/environments/eesm/finite_sc_eesm.rst +++ b/docs/parts/environments/eesm/finite_sc_eesm.rst @@ -1,4 +1,4 @@ Finite Control Set Speed Control Externally Excited Synchronous Motor Environment ******************************************************************************** -.. autoclass:: gym_electric_motor.envs.FiniteSpeedControlPermanentMagnetSynchronousMotorEnv +.. autoclass:: gym_electric_motor.envs.FiniteSpeedControlExternallyExcitedSynchronousMotorEnv :members: diff --git a/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py index 285250db..0baecb00 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py @@ -36,7 +36,7 @@ class ContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` Reference Variables: - ``['i_sd', 'i_sq']`` + ``['i_sd', 'i_sq', 'i_e']`` Control Cycle Time: tau = 1e-4 seconds @@ -48,7 +48,7 @@ class ContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro Box(low= 15 * [-1], high= 15 * [1]) Reference Space: - Box(low=[-1, -1, -1], high=[1, 1, 1]) + Box(low=[-1, -1, 0], high=[1, 1, 1]) Action Space: Box(low=[-1, -1, -1], high=[1, 1, 1]) @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g., 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,19 +112,19 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) """ default_subgenerators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), WienerProcessReferenceGenerator(reference_state='i_sq'), - WienerProcessReferenceGenerator(reference_state='i_e'), + WienerProcessReferenceGenerator(reference_state='i_e', limit_margin=(0, 1)), ) default_subconverters = ( ps.ContB6BridgeConverter(), diff --git a/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py index 1f52168e..f3076818 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g., 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,14 +112,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) """ default_subconverters = ( ps.ContB6BridgeConverter(), diff --git a/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py index 495df721..af05575c 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py @@ -100,8 +100,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g., 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -113,14 +113,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) """ default_subconverters = ( ps.ContB6BridgeConverter(), diff --git a/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py index fe4ba82e..e29da4f6 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g., 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,14 +112,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) """ default_subgenerators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), diff --git a/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py index 267fd2a4..7ba3e84c 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py @@ -36,7 +36,7 @@ class FiniteSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` Reference Variables: - ``['i_sd', 'i_sq', 'i_e']`` + ``['omega']`` Control Cycle Time: tau = 1e-5 seconds @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g., 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,14 +112,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) """ default_subconverters = ( ps.FiniteB6BridgeConverter(), diff --git a/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py index 11062e99..1152d0d1 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py @@ -36,7 +36,7 @@ class FiniteTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` Reference Variables: - ``['i_sd', 'i_sq', 'i_e']`` + ``['torque']`` Control Cycle Time: tau = 1e-5 seconds @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g., 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,14 +112,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) """ default_subconverters = ( ps.FiniteB6BridgeConverter(), diff --git a/gym_electric_motor/physical_systems/__init__.py b/gym_electric_motor/physical_systems/__init__.py index b91b743a..dc950f91 100644 --- a/gym_electric_motor/physical_systems/__init__.py +++ b/gym_electric_motor/physical_systems/__init__.py @@ -2,9 +2,10 @@ from .physical_systems import DcMotorSystem, SynchronousMotorSystem, SquirrelCageInductionMotorSystem, DoublyFedInductionMotorSystem, \ ExternallyExcitedSynchronousMotorSystem, ThreePhaseMotorSystem, SCMLSystem -from .converters import PowerElectronicConverter, FiniteOneQuadrantConverter, FiniteTwoQuadrantConverter, \ +from .converters import PowerElectronicConverter, FiniteOneQuadrantConverter, FiniteTwoCurrentQuadrantConverter, \ FiniteFourQuadrantConverter, FiniteMultiConverter, FiniteB6BridgeConverter, ContOneQuadrantConverter, \ - ContTwoQuadrantConverter, ContFourQuadrantConverter, ContMultiConverter, ContB6BridgeConverter, NoConverter + ContTwoCurrentQuadrantConverter, ContTwoVoltageQuadrantConverter, ContFourQuadrantConverter, ContMultiConverter,\ + ContB6BridgeConverter, NoConverter, FiniteTwoVoltageQuadrantConverter from .electric_motors import DcExternallyExcitedMotor, DcSeriesMotor, DcPermanentlyExcitedMotor, DcShuntMotor, \ PermanentMagnetSynchronousMotor, ElectricMotor, SynchronousReluctanceMotor, SquirrelCageInductionMotor, \ @@ -36,8 +37,10 @@ register_class(FiniteOneQuadrantConverter, PowerElectronicConverter, 'Finite-1QC') register_class(ContOneQuadrantConverter, PowerElectronicConverter, 'Cont-1QC') -register_class(FiniteTwoQuadrantConverter, PowerElectronicConverter, 'Finite-2QC') -register_class(ContTwoQuadrantConverter, PowerElectronicConverter, 'Cont-2QC') +register_class(FiniteTwoCurrentQuadrantConverter, PowerElectronicConverter, 'Finite-2CQC') +register_class(ContTwoCurrentQuadrantConverter, PowerElectronicConverter, 'Cont-2CQC') +register_class(FiniteTwoVoltageQuadrantConverter, PowerElectronicConverter, 'Finite-2VQC') +register_class(ContTwoVoltageQuadrantConverter, PowerElectronicConverter, 'Cont-2VQC') register_class(FiniteFourQuadrantConverter, PowerElectronicConverter, 'Finite-4QC') register_class(ContFourQuadrantConverter, PowerElectronicConverter, 'Cont-4QC') register_class(FiniteMultiConverter, PowerElectronicConverter, 'Finite-Multi') diff --git a/gym_electric_motor/physical_systems/converters.py b/gym_electric_motor/physical_systems/converters.py index 23d44a3e..07483e90 100644 --- a/gym_electric_motor/physical_systems/converters.py +++ b/gym_electric_motor/physical_systems/converters.py @@ -234,7 +234,7 @@ def i_sup(self, i_out): return i_out[0] if self._current_action == 1 else 0 -class FiniteTwoQuadrantConverter(FiniteConverter): +class FiniteTwoCurrentQuadrantConverter(FiniteConverter): """ Key: 'Finite-2QC' @@ -300,6 +300,46 @@ def _set_switching_pattern(self, action): self._switching_pattern = [0, action] return [self._action_start_time + self._interlocking_time, self._action_start_time + self._tau] +class FiniteTwoVoltageQuadrantConverter(FiniteConverter): + """ + Key: + 'Finite-2VQC' + + Switching States / Actions: + | 0: Both Transistors on. + | 1: Both Transistors off. + + Action Space: + Discrete(2) + + Output Voltages and Currents: + | voltages: Box(-q, 1, shape=(1,)) + | currents: Box(0, 1, shape=(1,)) + """ + + voltages = Box(-1, 1, shape=(1,), dtype=np.float64) + currents = Box(0, 1, shape=(1,), dtype=np.float64) + action_space = Discrete(2) + + def convert(self, i_out, t): + # Docstring in base class + if self._switching_pattern[0] == 0: + return [-1.0] + else: + return [1.0] + + def i_sup(self, i_out): + # Docstring in base class + if self._switching_pattern[0] == 0: + return -i_out[0] + else: + return i_out + + def _set_switching_pattern(self, action): + # Docstring in base class + self._switching_pattern = [action] + return [self._action_start_time + self._tau] + class FiniteFourQuadrantConverter(FiniteConverter): """ @@ -326,7 +366,7 @@ class FiniteFourQuadrantConverter(FiniteConverter): def __init__(self, **kwargs): # Docstring in base class super().__init__(**kwargs) - self._subconverters = [FiniteTwoQuadrantConverter(**kwargs), FiniteTwoQuadrantConverter(**kwargs)] + self._subconverters = [FiniteTwoCurrentQuadrantConverter(**kwargs), FiniteTwoCurrentQuadrantConverter(**kwargs)] def reset(self): # Docstring in base class @@ -386,10 +426,10 @@ def i_sup(self, i_out): return self._current_action[0] * i_out[0] -class ContTwoQuadrantConverter(ContDynamicallyAveragedConverter): +class ContTwoCurrentQuadrantConverter(ContDynamicallyAveragedConverter): """ Key: - 'Cont-2QC' + 'Cont-2CQC' Actions: | Duty Cycle upper Transistor: Action @@ -418,6 +458,32 @@ def i_sup(self, i_out): + self._interlocking_time / self._tau * (interlocking_current - self._current_action[0]) ) * i_out[0] +class ContTwoVoltageQuadrantConverter(ContDynamicallyAveragedConverter): + """ + Key: + 'Cont-2VQC' + + Actions: + | Duty Cycle Transistors on: Action + + Action Space: + Box([-1,1]) + + Output Voltages and Currents: + | voltages: Box(-1, 1, shape=(1,)) + | currents: Box(0, 1, shape=(1,)) + """ + voltages = Box(-1, 1, shape=(1,), dtype=np.float64) + currents = Box(0, 1, shape=(1,), dtype=np.float64) + action_space = Box(-1, 1, shape=(1,), dtype=np.float64) + + def convert(self, i_out, t): + # Docstring in base class + return self._current_action if i_out[0] > 0 else [0.0] + + def i_sup(self, i_out): + # Docstring in base class + return self._current_action[0] * i_out[0] class ContFourQuadrantConverter(ContDynamicallyAveragedConverter): """ @@ -446,7 +512,7 @@ class ContFourQuadrantConverter(ContDynamicallyAveragedConverter): def __init__(self, **kwargs): # Docstring in base class super().__init__(**kwargs) - self._subconverters = [ContTwoQuadrantConverter(**kwargs), ContTwoQuadrantConverter(**kwargs)] + self._subconverters = [ContTwoCurrentQuadrantConverter(**kwargs), ContTwoCurrentQuadrantConverter(**kwargs)] def _convert(self, *_): # Not used here @@ -774,9 +840,9 @@ def __init__(self, tau=1e-5, **kwargs): # Docstring in base class super().__init__(tau=tau, **kwargs) self._sub_converters = [ - FiniteTwoQuadrantConverter(tau=tau, **kwargs), - FiniteTwoQuadrantConverter(tau=tau, **kwargs), - FiniteTwoQuadrantConverter(tau=tau, **kwargs), + FiniteTwoCurrentQuadrantConverter(tau=tau, **kwargs), + FiniteTwoCurrentQuadrantConverter(tau=tau, **kwargs), + FiniteTwoCurrentQuadrantConverter(tau=tau, **kwargs), ] def reset(self): @@ -845,9 +911,9 @@ def __init__(self, tau=1e-4, **kwargs): # Docstring in base class super().__init__(tau=tau, **kwargs) self._subconverters = [ - ContTwoQuadrantConverter(tau=tau, **kwargs), - ContTwoQuadrantConverter(tau=tau, **kwargs), - ContTwoQuadrantConverter(tau=tau, **kwargs), + ContTwoCurrentQuadrantConverter(tau=tau, **kwargs), + ContTwoCurrentQuadrantConverter(tau=tau, **kwargs), + ContTwoCurrentQuadrantConverter(tau=tau, **kwargs), ] def reset(self): diff --git a/tests/test_physical_systems/test_converters.py b/tests/test_physical_systems/test_converters.py index 10695061..86629294 100644 --- a/tests/test_physical_systems/test_converters.py +++ b/tests/test_physical_systems/test_converters.py @@ -154,7 +154,7 @@ def test_discrete_single_power_electronic_converter( @pytest.mark.parametrize("convert, convert_class", [ ('Finite-1QC', cv.FiniteOneQuadrantConverter), - ('Finite-2QC', cv.FiniteTwoQuadrantConverter), + ('Finite-2QC', cv.FiniteTwoCurrentQuadrantConverter), ('Finite-4QC', cv.FiniteFourQuadrantConverter) ]) @pytest.mark.parametrize("tau", g_taus) @@ -746,7 +746,7 @@ def test_i_sup(self, converter, i_sup): class TestFiniteTwoQuadrantConverter(TestFiniteConverter): - class_to_test = cv.FiniteTwoQuadrantConverter + class_to_test = cv.FiniteTwoCurrentQuadrantConverter key = 'Finite-2QC' @pytest.mark.parametrize("interlocking_time", [0.0, 0.1]) @@ -897,7 +897,7 @@ def test_i_sup(self, monkeypatch, converter, i_out): class TestContTwoQuadrantConverter(TestContDynamicallyAveragedConverter): - class_to_test = cv.ContTwoQuadrantConverter + class_to_test = cv.ContTwoCurrentQuadrantConverter key = 'Cont-2QC' @pytest.mark.parametrize('interlocking_time', [0.0, 0.1, 1]) From e53c1181fef14c572ebb861e7568660753b092e5 Mon Sep 17 00:00:00 2001 From: Arne Date: Tue, 22 Mar 2022 20:48:27 +0100 Subject: [PATCH 57/79] test fixes --- .../test_physical_systems/test_converters.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/test_physical_systems/test_converters.py b/tests/test_physical_systems/test_converters.py index 86629294..6c45e86d 100644 --- a/tests/test_physical_systems/test_converters.py +++ b/tests/test_physical_systems/test_converters.py @@ -52,13 +52,13 @@ g_2qc_test_voltages = [g_test_voltages_2qc, g_test_voltages_2qc_interlocking] g_4qc_test_voltages = [g_test_voltages_4qc, g_test_voltages_4qc_interlocking] g_disc_test_voltages = {'Finite-1QC': g_1qc_test_voltages, - 'Finite-2QC': g_2qc_test_voltages, + 'Finite-2CQC': g_2qc_test_voltages, 'Finite-4QC': g_4qc_test_voltages} g_disc_test_i_ins = {'Finite-1QC': g_i_ins_1qc, - 'Finite-2QC': g_i_ins_2qc, + 'Finite-2CQC': g_i_ins_2qc, 'Finite-4QC': g_i_ins_4qc} g_disc_test_actions = {'Finite-1QC': g_actions_1qc, - 'Finite-2QC': g_actions_2qc, + 'Finite-2CQC': g_actions_2qc, 'Finite-4QC': g_actions_4qc} @@ -109,7 +109,7 @@ def discrete_converter_functions_testing( @pytest.mark.parametrize("interlocking_time", g_interlocking_times) @pytest.mark.parametrize("converter_type, action_space_n, actions, i_ins, test_voltages", [('Finite-1QC', 2, g_actions_1qc, g_i_ins_1qc, g_1qc_test_voltages), - ('Finite-2QC', 3, g_actions_2qc, g_i_ins_2qc, g_2qc_test_voltages), + ('Finite-2CQC', 3, g_actions_2qc, g_i_ins_2qc, g_2qc_test_voltages), ('Finite-4QC', 4, g_actions_4qc, g_i_ins_4qc, g_4qc_test_voltages)]) def test_discrete_single_power_electronic_converter( converter_type, action_space_n, actions, i_ins, test_voltages, interlocking_time, tau @@ -154,7 +154,7 @@ def test_discrete_single_power_electronic_converter( @pytest.mark.parametrize("convert, convert_class", [ ('Finite-1QC', cv.FiniteOneQuadrantConverter), - ('Finite-2QC', cv.FiniteTwoCurrentQuadrantConverter), + ('Finite-2CQC', cv.FiniteTwoCurrentQuadrantConverter), ('Finite-4QC', cv.FiniteFourQuadrantConverter) ]) @pytest.mark.parametrize("tau", g_taus) @@ -194,7 +194,7 @@ def test_discrete_multi_converter_initializations(tau, interlocking_time): :return: """ # define all converter - all_single_disc_converter = ['Finite-1QC', 'Finite-2QC', 'Finite-4QC', 'Finite-B6C'] + all_single_disc_converter = ['Finite-1QC', 'Finite-2CQC', 'Finite-4QC', 'Finite-B6C'] interlocking_time *= tau # chose every combination of single converters for conv_1 in all_single_disc_converter: @@ -218,7 +218,7 @@ def test_discrete_multi_power_electronic_converter(tau, interlocking_time): :return: """ # define all converter - all_single_disc_converter = ['Finite-1QC', 'Finite-2QC', 'Finite-4QC', 'Finite-B6C'] + all_single_disc_converter = ['Finite-1QC', 'Finite-2CQC', 'Finite-4QC', 'Finite-B6C'] interlocking_time *= tau for conv_0 in all_single_disc_converter: @@ -265,7 +265,7 @@ def test_discrete_multi_power_electronic_converter(tau, interlocking_time): # region continuous converter -@pytest.mark.parametrize("converter_type", ['Cont-1QC', 'Cont-2QC', 'Cont-4QC']) +@pytest.mark.parametrize("converter_type", ['Cont-1QC', 'Cont-2CQC', 'Cont-4QC']) @pytest.mark.parametrize("tau", g_taus) @pytest.mark.parametrize("interlocking_time", g_interlocking_times) def test_continuous_power_electronic_converter(converter_type, tau, interlocking_time): @@ -327,7 +327,7 @@ def continuous_converter_functions_testing(converter, times, interlocking_time, def comparable_voltage(converter_type, action, i_in, tau, interlocking_time, last_action): voltage = np.array([action]) error = np.array([- np.sign(i_in) / tau * interlocking_time]) - if converter_type == 'Cont-2QC': + if converter_type == 'Cont-2CQC': voltage += error voltage = max(min(voltage, np.array([1])), np.array([0])) elif converter_type == 'Cont-4QC': @@ -348,7 +348,7 @@ def test_continuous_multi_power_electronic_converter(tau, interlocking_time): :return: """ # define all converter - all_single_cont_converter = ['Cont-1QC', 'Cont-2QC', 'Cont-4QC', 'Cont-B6C'] + all_single_cont_converter = ['Cont-1QC', 'Cont-2CQC', 'Cont-4QC', 'Cont-B6C'] interlocking_time *= tau times = g_times_cont * tau for conv_1 in all_single_cont_converter: @@ -747,7 +747,7 @@ def test_i_sup(self, converter, i_sup): class TestFiniteTwoQuadrantConverter(TestFiniteConverter): class_to_test = cv.FiniteTwoCurrentQuadrantConverter - key = 'Finite-2QC' + key = 'Finite-2CQC' @pytest.mark.parametrize("interlocking_time", [0.0, 0.1]) def test_set_switching_pattern(self, monkeypatch, converter, interlocking_time): @@ -898,7 +898,7 @@ def test_i_sup(self, monkeypatch, converter, i_out): class TestContTwoQuadrantConverter(TestContDynamicallyAveragedConverter): class_to_test = cv.ContTwoCurrentQuadrantConverter - key = 'Cont-2QC' + key = 'Cont-2CQC' @pytest.mark.parametrize('interlocking_time', [0.0, 0.1, 1]) @pytest.mark.parametrize('i_out', [[0.0], [0.1], [-1]]) @@ -1022,7 +1022,7 @@ def test_set_action(self, monkeypatch, converter, **_): assert sc2.action_set_time == t def test_default_init(self): - converter = self.class_to_test(subconverters=['Finite-1QC', 'Finite-B6C', 'Finite-2QC']) + converter = self.class_to_test(subconverters=['Finite-1QC', 'Finite-B6C', 'Finite-2CQC']) assert converter._tau == 1e-5 @pytest.mark.parametrize('i_out', [[0, 6, 2, 7, 9], [1, 0.5, 2], [-1, 1]]) From 492b5b36eb58e618f2fbd431e3b943f3a8843749 Mon Sep 17 00:00:00 2001 From: Arne Date: Wed, 23 Mar 2022 20:09:38 +0100 Subject: [PATCH 58/79] 2VQC --- gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py | 2 +- gym_electric_motor/physical_systems/converters.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py index 0baecb00..62f3c6e9 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py @@ -128,7 +128,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) default_subconverters = ( ps.ContB6BridgeConverter(), - ps.ContFourQuadrantConverter() + ps.ContTwoVoltageQuadrantConverter() ) physical_system = ExternallyExcitedSynchronousMotorSystem( supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=300.0)), diff --git a/gym_electric_motor/physical_systems/converters.py b/gym_electric_motor/physical_systems/converters.py index 07483e90..a56e705a 100644 --- a/gym_electric_motor/physical_systems/converters.py +++ b/gym_electric_motor/physical_systems/converters.py @@ -479,7 +479,7 @@ class ContTwoVoltageQuadrantConverter(ContDynamicallyAveragedConverter): def convert(self, i_out, t): # Docstring in base class - return self._current_action if i_out[0] > 0 else [0.0] + return self._current_action.tolist() if i_out[0] >= 0 else [0.0] def i_sup(self, i_out): # Docstring in base class From 7da863d0eccd9195b1c9a121472d5c184dca5f78 Mon Sep 17 00:00:00 2001 From: Arne Date: Sun, 3 Apr 2022 11:29:31 +0200 Subject: [PATCH 59/79] Pushback to zero current for "2VQC" --- gym_electric_motor/physical_systems/converters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym_electric_motor/physical_systems/converters.py b/gym_electric_motor/physical_systems/converters.py index a56e705a..5e369547 100644 --- a/gym_electric_motor/physical_systems/converters.py +++ b/gym_electric_motor/physical_systems/converters.py @@ -479,7 +479,7 @@ class ContTwoVoltageQuadrantConverter(ContDynamicallyAveragedConverter): def convert(self, i_out, t): # Docstring in base class - return self._current_action.tolist() if i_out[0] >= 0 else [0.0] + return self._current_action.tolist() if i_out[0] >= 0.0 else [-0.01 * i_out[0]] def i_sup(self, i_out): # Docstring in base class From 973091ac509ae9241ee70b69e9996448bd0c87a8 Mon Sep 17 00:00:00 2001 From: Arne Date: Tue, 5 Apr 2022 20:25:38 +0200 Subject: [PATCH 60/79] Revert "Pushback to zero current for "2VQC"" This reverts commit 7da863d0eccd9195b1c9a121472d5c184dca5f78. --- gym_electric_motor/physical_systems/converters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym_electric_motor/physical_systems/converters.py b/gym_electric_motor/physical_systems/converters.py index 5e369547..a56e705a 100644 --- a/gym_electric_motor/physical_systems/converters.py +++ b/gym_electric_motor/physical_systems/converters.py @@ -479,7 +479,7 @@ class ContTwoVoltageQuadrantConverter(ContDynamicallyAveragedConverter): def convert(self, i_out, t): # Docstring in base class - return self._current_action.tolist() if i_out[0] >= 0.0 else [-0.01 * i_out[0]] + return self._current_action.tolist() if i_out[0] >= 0 else [0.0] def i_sup(self, i_out): # Docstring in base class From 7c53ea101c03005decf7e39f1be781ab307bb865 Mon Sep 17 00:00:00 2001 From: Arne Date: Tue, 5 Apr 2022 20:26:30 +0200 Subject: [PATCH 61/79] Revert "2VQC" This reverts commit 492b5b36eb58e618f2fbd431e3b943f3a8843749. --- gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py | 2 +- gym_electric_motor/physical_systems/converters.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py index 62f3c6e9..0baecb00 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py @@ -128,7 +128,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve ) default_subconverters = ( ps.ContB6BridgeConverter(), - ps.ContTwoVoltageQuadrantConverter() + ps.ContFourQuadrantConverter() ) physical_system = ExternallyExcitedSynchronousMotorSystem( supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=300.0)), diff --git a/gym_electric_motor/physical_systems/converters.py b/gym_electric_motor/physical_systems/converters.py index a56e705a..07483e90 100644 --- a/gym_electric_motor/physical_systems/converters.py +++ b/gym_electric_motor/physical_systems/converters.py @@ -479,7 +479,7 @@ class ContTwoVoltageQuadrantConverter(ContDynamicallyAveragedConverter): def convert(self, i_out, t): # Docstring in base class - return self._current_action.tolist() if i_out[0] >= 0 else [0.0] + return self._current_action if i_out[0] > 0 else [0.0] def i_sup(self, i_out): # Docstring in base class From 34fe77bf09e1e7c125e8ee68198955c996e05f23 Mon Sep 17 00:00:00 2001 From: Arne Date: Tue, 5 Apr 2022 20:26:49 +0200 Subject: [PATCH 62/79] Revert "test fixes" This reverts commit e53c1181fef14c572ebb861e7568660753b092e5. --- .../test_physical_systems/test_converters.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/test_physical_systems/test_converters.py b/tests/test_physical_systems/test_converters.py index 6c45e86d..86629294 100644 --- a/tests/test_physical_systems/test_converters.py +++ b/tests/test_physical_systems/test_converters.py @@ -52,13 +52,13 @@ g_2qc_test_voltages = [g_test_voltages_2qc, g_test_voltages_2qc_interlocking] g_4qc_test_voltages = [g_test_voltages_4qc, g_test_voltages_4qc_interlocking] g_disc_test_voltages = {'Finite-1QC': g_1qc_test_voltages, - 'Finite-2CQC': g_2qc_test_voltages, + 'Finite-2QC': g_2qc_test_voltages, 'Finite-4QC': g_4qc_test_voltages} g_disc_test_i_ins = {'Finite-1QC': g_i_ins_1qc, - 'Finite-2CQC': g_i_ins_2qc, + 'Finite-2QC': g_i_ins_2qc, 'Finite-4QC': g_i_ins_4qc} g_disc_test_actions = {'Finite-1QC': g_actions_1qc, - 'Finite-2CQC': g_actions_2qc, + 'Finite-2QC': g_actions_2qc, 'Finite-4QC': g_actions_4qc} @@ -109,7 +109,7 @@ def discrete_converter_functions_testing( @pytest.mark.parametrize("interlocking_time", g_interlocking_times) @pytest.mark.parametrize("converter_type, action_space_n, actions, i_ins, test_voltages", [('Finite-1QC', 2, g_actions_1qc, g_i_ins_1qc, g_1qc_test_voltages), - ('Finite-2CQC', 3, g_actions_2qc, g_i_ins_2qc, g_2qc_test_voltages), + ('Finite-2QC', 3, g_actions_2qc, g_i_ins_2qc, g_2qc_test_voltages), ('Finite-4QC', 4, g_actions_4qc, g_i_ins_4qc, g_4qc_test_voltages)]) def test_discrete_single_power_electronic_converter( converter_type, action_space_n, actions, i_ins, test_voltages, interlocking_time, tau @@ -154,7 +154,7 @@ def test_discrete_single_power_electronic_converter( @pytest.mark.parametrize("convert, convert_class", [ ('Finite-1QC', cv.FiniteOneQuadrantConverter), - ('Finite-2CQC', cv.FiniteTwoCurrentQuadrantConverter), + ('Finite-2QC', cv.FiniteTwoCurrentQuadrantConverter), ('Finite-4QC', cv.FiniteFourQuadrantConverter) ]) @pytest.mark.parametrize("tau", g_taus) @@ -194,7 +194,7 @@ def test_discrete_multi_converter_initializations(tau, interlocking_time): :return: """ # define all converter - all_single_disc_converter = ['Finite-1QC', 'Finite-2CQC', 'Finite-4QC', 'Finite-B6C'] + all_single_disc_converter = ['Finite-1QC', 'Finite-2QC', 'Finite-4QC', 'Finite-B6C'] interlocking_time *= tau # chose every combination of single converters for conv_1 in all_single_disc_converter: @@ -218,7 +218,7 @@ def test_discrete_multi_power_electronic_converter(tau, interlocking_time): :return: """ # define all converter - all_single_disc_converter = ['Finite-1QC', 'Finite-2CQC', 'Finite-4QC', 'Finite-B6C'] + all_single_disc_converter = ['Finite-1QC', 'Finite-2QC', 'Finite-4QC', 'Finite-B6C'] interlocking_time *= tau for conv_0 in all_single_disc_converter: @@ -265,7 +265,7 @@ def test_discrete_multi_power_electronic_converter(tau, interlocking_time): # region continuous converter -@pytest.mark.parametrize("converter_type", ['Cont-1QC', 'Cont-2CQC', 'Cont-4QC']) +@pytest.mark.parametrize("converter_type", ['Cont-1QC', 'Cont-2QC', 'Cont-4QC']) @pytest.mark.parametrize("tau", g_taus) @pytest.mark.parametrize("interlocking_time", g_interlocking_times) def test_continuous_power_electronic_converter(converter_type, tau, interlocking_time): @@ -327,7 +327,7 @@ def continuous_converter_functions_testing(converter, times, interlocking_time, def comparable_voltage(converter_type, action, i_in, tau, interlocking_time, last_action): voltage = np.array([action]) error = np.array([- np.sign(i_in) / tau * interlocking_time]) - if converter_type == 'Cont-2CQC': + if converter_type == 'Cont-2QC': voltage += error voltage = max(min(voltage, np.array([1])), np.array([0])) elif converter_type == 'Cont-4QC': @@ -348,7 +348,7 @@ def test_continuous_multi_power_electronic_converter(tau, interlocking_time): :return: """ # define all converter - all_single_cont_converter = ['Cont-1QC', 'Cont-2CQC', 'Cont-4QC', 'Cont-B6C'] + all_single_cont_converter = ['Cont-1QC', 'Cont-2QC', 'Cont-4QC', 'Cont-B6C'] interlocking_time *= tau times = g_times_cont * tau for conv_1 in all_single_cont_converter: @@ -747,7 +747,7 @@ def test_i_sup(self, converter, i_sup): class TestFiniteTwoQuadrantConverter(TestFiniteConverter): class_to_test = cv.FiniteTwoCurrentQuadrantConverter - key = 'Finite-2CQC' + key = 'Finite-2QC' @pytest.mark.parametrize("interlocking_time", [0.0, 0.1]) def test_set_switching_pattern(self, monkeypatch, converter, interlocking_time): @@ -898,7 +898,7 @@ def test_i_sup(self, monkeypatch, converter, i_out): class TestContTwoQuadrantConverter(TestContDynamicallyAveragedConverter): class_to_test = cv.ContTwoCurrentQuadrantConverter - key = 'Cont-2CQC' + key = 'Cont-2QC' @pytest.mark.parametrize('interlocking_time', [0.0, 0.1, 1]) @pytest.mark.parametrize('i_out', [[0.0], [0.1], [-1]]) @@ -1022,7 +1022,7 @@ def test_set_action(self, monkeypatch, converter, **_): assert sc2.action_set_time == t def test_default_init(self): - converter = self.class_to_test(subconverters=['Finite-1QC', 'Finite-B6C', 'Finite-2CQC']) + converter = self.class_to_test(subconverters=['Finite-1QC', 'Finite-B6C', 'Finite-2QC']) assert converter._tau == 1e-5 @pytest.mark.parametrize('i_out', [[0, 6, 2, 7, 9], [1, 0.5, 2], [-1, 1]]) From fdd3d3d355d4efa729565e8b1b73c62b5d277678 Mon Sep 17 00:00:00 2001 From: Arne Date: Tue, 5 Apr 2022 20:27:04 +0200 Subject: [PATCH 63/79] Revert "TwoVoltageQuadrantConverter and comment fixes" This reverts commit bffd6a78b577ec1a8168a5fcd4743ec777527a3d. --- .../environments/eesm/finite_sc_eesm.rst | 2 +- .../envs/gym_eesm/cont_cc_eesm_env.py | 16 ++-- .../envs/gym_eesm/cont_sc_eesm_env.py | 10 +-- .../envs/gym_eesm/cont_tc_eesm_env.py | 10 +-- .../envs/gym_eesm/finite_cc_eesm_env.py | 10 +-- .../envs/gym_eesm/finite_sc_eesm_env.py | 12 +-- .../envs/gym_eesm/finite_tc_eesm_env.py | 12 +-- .../physical_systems/__init__.py | 11 +-- .../physical_systems/converters.py | 88 +++---------------- .../test_physical_systems/test_converters.py | 6 +- 10 files changed, 54 insertions(+), 123 deletions(-) diff --git a/docs/parts/environments/eesm/finite_sc_eesm.rst b/docs/parts/environments/eesm/finite_sc_eesm.rst index 52cb5585..c66cfd4a 100644 --- a/docs/parts/environments/eesm/finite_sc_eesm.rst +++ b/docs/parts/environments/eesm/finite_sc_eesm.rst @@ -1,4 +1,4 @@ Finite Control Set Speed Control Externally Excited Synchronous Motor Environment ******************************************************************************** -.. autoclass:: gym_electric_motor.envs.FiniteSpeedControlExternallyExcitedSynchronousMotorEnv +.. autoclass:: gym_electric_motor.envs.FiniteSpeedControlPermanentMagnetSynchronousMotorEnv :members: diff --git a/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py index 0baecb00..285250db 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py @@ -36,7 +36,7 @@ class ContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` Reference Variables: - ``['i_sd', 'i_sq', 'i_e']`` + ``['i_sd', 'i_sq']`` Control Cycle Time: tau = 1e-4 seconds @@ -48,7 +48,7 @@ class ContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro Box(low= 15 * [-1], high= 15 * [1]) Reference Space: - Box(low=[-1, -1, 0], high=[1, 1, 1]) + Box(low=[-1, -1, -1], high=[1, 1, 1]) Action Space: Box(low=[-1, -1, -1], high=[1, 1, 1]) @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g., 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,19 +112,19 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ default_subgenerators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), WienerProcessReferenceGenerator(reference_state='i_sq'), - WienerProcessReferenceGenerator(reference_state='i_e', limit_margin=(0, 1)), + WienerProcessReferenceGenerator(reference_state='i_e'), ) default_subconverters = ( ps.ContB6BridgeConverter(), diff --git a/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py index f3076818..1f52168e 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g., 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,14 +112,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ default_subconverters = ( ps.ContB6BridgeConverter(), diff --git a/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py index af05575c..495df721 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py @@ -100,8 +100,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g., 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -113,14 +113,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ default_subconverters = ( ps.ContB6BridgeConverter(), diff --git a/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py index e29da4f6..fe4ba82e 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g., 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,14 +112,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ default_subgenerators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), diff --git a/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py index 7ba3e84c..267fd2a4 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py @@ -36,7 +36,7 @@ class FiniteSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` Reference Variables: - ``['omega']`` + ``['i_sd', 'i_sq', 'i_e']`` Control Cycle Time: tau = 1e-5 seconds @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g., 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,14 +112,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ default_subconverters = ( ps.FiniteB6BridgeConverter(), diff --git a/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py index 1152d0d1..11062e99 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py @@ -36,7 +36,7 @@ class FiniteTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` Reference Variables: - ``['torque']`` + ``['i_sd', 'i_sq', 'i_e']`` Control Cycle Time: tau = 1e-5 seconds @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g., 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,14 +112,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) """ default_subconverters = ( ps.FiniteB6BridgeConverter(), diff --git a/gym_electric_motor/physical_systems/__init__.py b/gym_electric_motor/physical_systems/__init__.py index dc950f91..b91b743a 100644 --- a/gym_electric_motor/physical_systems/__init__.py +++ b/gym_electric_motor/physical_systems/__init__.py @@ -2,10 +2,9 @@ from .physical_systems import DcMotorSystem, SynchronousMotorSystem, SquirrelCageInductionMotorSystem, DoublyFedInductionMotorSystem, \ ExternallyExcitedSynchronousMotorSystem, ThreePhaseMotorSystem, SCMLSystem -from .converters import PowerElectronicConverter, FiniteOneQuadrantConverter, FiniteTwoCurrentQuadrantConverter, \ +from .converters import PowerElectronicConverter, FiniteOneQuadrantConverter, FiniteTwoQuadrantConverter, \ FiniteFourQuadrantConverter, FiniteMultiConverter, FiniteB6BridgeConverter, ContOneQuadrantConverter, \ - ContTwoCurrentQuadrantConverter, ContTwoVoltageQuadrantConverter, ContFourQuadrantConverter, ContMultiConverter,\ - ContB6BridgeConverter, NoConverter, FiniteTwoVoltageQuadrantConverter + ContTwoQuadrantConverter, ContFourQuadrantConverter, ContMultiConverter, ContB6BridgeConverter, NoConverter from .electric_motors import DcExternallyExcitedMotor, DcSeriesMotor, DcPermanentlyExcitedMotor, DcShuntMotor, \ PermanentMagnetSynchronousMotor, ElectricMotor, SynchronousReluctanceMotor, SquirrelCageInductionMotor, \ @@ -37,10 +36,8 @@ register_class(FiniteOneQuadrantConverter, PowerElectronicConverter, 'Finite-1QC') register_class(ContOneQuadrantConverter, PowerElectronicConverter, 'Cont-1QC') -register_class(FiniteTwoCurrentQuadrantConverter, PowerElectronicConverter, 'Finite-2CQC') -register_class(ContTwoCurrentQuadrantConverter, PowerElectronicConverter, 'Cont-2CQC') -register_class(FiniteTwoVoltageQuadrantConverter, PowerElectronicConverter, 'Finite-2VQC') -register_class(ContTwoVoltageQuadrantConverter, PowerElectronicConverter, 'Cont-2VQC') +register_class(FiniteTwoQuadrantConverter, PowerElectronicConverter, 'Finite-2QC') +register_class(ContTwoQuadrantConverter, PowerElectronicConverter, 'Cont-2QC') register_class(FiniteFourQuadrantConverter, PowerElectronicConverter, 'Finite-4QC') register_class(ContFourQuadrantConverter, PowerElectronicConverter, 'Cont-4QC') register_class(FiniteMultiConverter, PowerElectronicConverter, 'Finite-Multi') diff --git a/gym_electric_motor/physical_systems/converters.py b/gym_electric_motor/physical_systems/converters.py index 07483e90..23d44a3e 100644 --- a/gym_electric_motor/physical_systems/converters.py +++ b/gym_electric_motor/physical_systems/converters.py @@ -234,7 +234,7 @@ def i_sup(self, i_out): return i_out[0] if self._current_action == 1 else 0 -class FiniteTwoCurrentQuadrantConverter(FiniteConverter): +class FiniteTwoQuadrantConverter(FiniteConverter): """ Key: 'Finite-2QC' @@ -300,46 +300,6 @@ def _set_switching_pattern(self, action): self._switching_pattern = [0, action] return [self._action_start_time + self._interlocking_time, self._action_start_time + self._tau] -class FiniteTwoVoltageQuadrantConverter(FiniteConverter): - """ - Key: - 'Finite-2VQC' - - Switching States / Actions: - | 0: Both Transistors on. - | 1: Both Transistors off. - - Action Space: - Discrete(2) - - Output Voltages and Currents: - | voltages: Box(-q, 1, shape=(1,)) - | currents: Box(0, 1, shape=(1,)) - """ - - voltages = Box(-1, 1, shape=(1,), dtype=np.float64) - currents = Box(0, 1, shape=(1,), dtype=np.float64) - action_space = Discrete(2) - - def convert(self, i_out, t): - # Docstring in base class - if self._switching_pattern[0] == 0: - return [-1.0] - else: - return [1.0] - - def i_sup(self, i_out): - # Docstring in base class - if self._switching_pattern[0] == 0: - return -i_out[0] - else: - return i_out - - def _set_switching_pattern(self, action): - # Docstring in base class - self._switching_pattern = [action] - return [self._action_start_time + self._tau] - class FiniteFourQuadrantConverter(FiniteConverter): """ @@ -366,7 +326,7 @@ class FiniteFourQuadrantConverter(FiniteConverter): def __init__(self, **kwargs): # Docstring in base class super().__init__(**kwargs) - self._subconverters = [FiniteTwoCurrentQuadrantConverter(**kwargs), FiniteTwoCurrentQuadrantConverter(**kwargs)] + self._subconverters = [FiniteTwoQuadrantConverter(**kwargs), FiniteTwoQuadrantConverter(**kwargs)] def reset(self): # Docstring in base class @@ -426,10 +386,10 @@ def i_sup(self, i_out): return self._current_action[0] * i_out[0] -class ContTwoCurrentQuadrantConverter(ContDynamicallyAveragedConverter): +class ContTwoQuadrantConverter(ContDynamicallyAveragedConverter): """ Key: - 'Cont-2CQC' + 'Cont-2QC' Actions: | Duty Cycle upper Transistor: Action @@ -458,32 +418,6 @@ def i_sup(self, i_out): + self._interlocking_time / self._tau * (interlocking_current - self._current_action[0]) ) * i_out[0] -class ContTwoVoltageQuadrantConverter(ContDynamicallyAveragedConverter): - """ - Key: - 'Cont-2VQC' - - Actions: - | Duty Cycle Transistors on: Action - - Action Space: - Box([-1,1]) - - Output Voltages and Currents: - | voltages: Box(-1, 1, shape=(1,)) - | currents: Box(0, 1, shape=(1,)) - """ - voltages = Box(-1, 1, shape=(1,), dtype=np.float64) - currents = Box(0, 1, shape=(1,), dtype=np.float64) - action_space = Box(-1, 1, shape=(1,), dtype=np.float64) - - def convert(self, i_out, t): - # Docstring in base class - return self._current_action if i_out[0] > 0 else [0.0] - - def i_sup(self, i_out): - # Docstring in base class - return self._current_action[0] * i_out[0] class ContFourQuadrantConverter(ContDynamicallyAveragedConverter): """ @@ -512,7 +446,7 @@ class ContFourQuadrantConverter(ContDynamicallyAveragedConverter): def __init__(self, **kwargs): # Docstring in base class super().__init__(**kwargs) - self._subconverters = [ContTwoCurrentQuadrantConverter(**kwargs), ContTwoCurrentQuadrantConverter(**kwargs)] + self._subconverters = [ContTwoQuadrantConverter(**kwargs), ContTwoQuadrantConverter(**kwargs)] def _convert(self, *_): # Not used here @@ -840,9 +774,9 @@ def __init__(self, tau=1e-5, **kwargs): # Docstring in base class super().__init__(tau=tau, **kwargs) self._sub_converters = [ - FiniteTwoCurrentQuadrantConverter(tau=tau, **kwargs), - FiniteTwoCurrentQuadrantConverter(tau=tau, **kwargs), - FiniteTwoCurrentQuadrantConverter(tau=tau, **kwargs), + FiniteTwoQuadrantConverter(tau=tau, **kwargs), + FiniteTwoQuadrantConverter(tau=tau, **kwargs), + FiniteTwoQuadrantConverter(tau=tau, **kwargs), ] def reset(self): @@ -911,9 +845,9 @@ def __init__(self, tau=1e-4, **kwargs): # Docstring in base class super().__init__(tau=tau, **kwargs) self._subconverters = [ - ContTwoCurrentQuadrantConverter(tau=tau, **kwargs), - ContTwoCurrentQuadrantConverter(tau=tau, **kwargs), - ContTwoCurrentQuadrantConverter(tau=tau, **kwargs), + ContTwoQuadrantConverter(tau=tau, **kwargs), + ContTwoQuadrantConverter(tau=tau, **kwargs), + ContTwoQuadrantConverter(tau=tau, **kwargs), ] def reset(self): diff --git a/tests/test_physical_systems/test_converters.py b/tests/test_physical_systems/test_converters.py index 86629294..10695061 100644 --- a/tests/test_physical_systems/test_converters.py +++ b/tests/test_physical_systems/test_converters.py @@ -154,7 +154,7 @@ def test_discrete_single_power_electronic_converter( @pytest.mark.parametrize("convert, convert_class", [ ('Finite-1QC', cv.FiniteOneQuadrantConverter), - ('Finite-2QC', cv.FiniteTwoCurrentQuadrantConverter), + ('Finite-2QC', cv.FiniteTwoQuadrantConverter), ('Finite-4QC', cv.FiniteFourQuadrantConverter) ]) @pytest.mark.parametrize("tau", g_taus) @@ -746,7 +746,7 @@ def test_i_sup(self, converter, i_sup): class TestFiniteTwoQuadrantConverter(TestFiniteConverter): - class_to_test = cv.FiniteTwoCurrentQuadrantConverter + class_to_test = cv.FiniteTwoQuadrantConverter key = 'Finite-2QC' @pytest.mark.parametrize("interlocking_time", [0.0, 0.1]) @@ -897,7 +897,7 @@ def test_i_sup(self, monkeypatch, converter, i_out): class TestContTwoQuadrantConverter(TestContDynamicallyAveragedConverter): - class_to_test = cv.ContTwoCurrentQuadrantConverter + class_to_test = cv.ContTwoQuadrantConverter key = 'Cont-2QC' @pytest.mark.parametrize('interlocking_time', [0.0, 0.1, 1]) From a7946bd1fcd535324e7ae4226ae49430468ebea5 Mon Sep 17 00:00:00 2001 From: Arne Date: Wed, 6 Apr 2022 20:01:54 +0200 Subject: [PATCH 64/79] Redo Undone changes --- docs/parts/callbacks.rst | 2 +- docs/parts/constraints/constraint.rst | 2 +- docs/parts/environments/eesm/finite_sc_eesm.rst | 4 ++-- .../envs/gym_eesm/cont_cc_eesm_env.py | 16 ++++++++-------- .../envs/gym_eesm/cont_sc_eesm_env.py | 10 +++++----- .../envs/gym_eesm/cont_tc_eesm_env.py | 10 +++++----- .../envs/gym_eesm/finite_cc_eesm_env.py | 10 +++++----- .../envs/gym_eesm/finite_sc_eesm_env.py | 12 ++++++------ .../envs/gym_eesm/finite_tc_eesm_env.py | 12 ++++++------ 9 files changed, 39 insertions(+), 39 deletions(-) diff --git a/docs/parts/callbacks.rst b/docs/parts/callbacks.rst index 50e29dd2..68c86fe9 100644 --- a/docs/parts/callbacks.rst +++ b/docs/parts/callbacks.rst @@ -1,5 +1,5 @@ Callbacks -##### +########## .. automodule:: gym_electric_motor.callbacks :members: diff --git a/docs/parts/constraints/constraint.rst b/docs/parts/constraints/constraint.rst index 03b09558..fcaf4240 100644 --- a/docs/parts/constraints/constraint.rst +++ b/docs/parts/constraints/constraint.rst @@ -2,7 +2,7 @@ Constraint Base Class ##################### How To: Define Your Own Constraints -________________________________ +___________________________________ Constraint API Documentation diff --git a/docs/parts/environments/eesm/finite_sc_eesm.rst b/docs/parts/environments/eesm/finite_sc_eesm.rst index c66cfd4a..666d02b4 100644 --- a/docs/parts/environments/eesm/finite_sc_eesm.rst +++ b/docs/parts/environments/eesm/finite_sc_eesm.rst @@ -1,4 +1,4 @@ Finite Control Set Speed Control Externally Excited Synchronous Motor Environment -******************************************************************************** -.. autoclass:: gym_electric_motor.envs.FiniteSpeedControlPermanentMagnetSynchronousMotorEnv +************************************************************************************ +.. autoclass:: gym_electric_motor.envs.FiniteSpeedControlExternallyExcitedSynchronousMotorEnv :members: diff --git a/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py index 285250db..0baecb00 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py @@ -36,7 +36,7 @@ class ContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` Reference Variables: - ``['i_sd', 'i_sq']`` + ``['i_sd', 'i_sq', 'i_e']`` Control Cycle Time: tau = 1e-4 seconds @@ -48,7 +48,7 @@ class ContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro Box(low= 15 * [-1], high= 15 * [1]) Reference Space: - Box(low=[-1, -1, -1], high=[1, 1, 1]) + Box(low=[-1, -1, 0], high=[1, 1, 1]) Action Space: Box(low=[-1, -1, -1], high=[1, 1, 1]) @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g., 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,19 +112,19 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) """ default_subgenerators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), WienerProcessReferenceGenerator(reference_state='i_sq'), - WienerProcessReferenceGenerator(reference_state='i_e'), + WienerProcessReferenceGenerator(reference_state='i_e', limit_margin=(0, 1)), ) default_subconverters = ( ps.ContB6BridgeConverter(), diff --git a/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py index 1f52168e..f3076818 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g., 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,14 +112,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) """ default_subconverters = ( ps.ContB6BridgeConverter(), diff --git a/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py index 495df721..af05575c 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py @@ -100,8 +100,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g., 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -113,14 +113,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) """ default_subconverters = ( ps.ContB6BridgeConverter(), diff --git a/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py index fe4ba82e..390eea74 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_cc_eesm_env.py @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g., 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g.,, the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,14 +112,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) """ default_subgenerators = ( WienerProcessReferenceGenerator(reference_state='i_sd'), diff --git a/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py index 267fd2a4..7ba3e84c 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_sc_eesm_env.py @@ -36,7 +36,7 @@ class FiniteSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` Reference Variables: - ``['i_sd', 'i_sq', 'i_e']`` + ``['omega']`` Control Cycle Time: tau = 1e-5 seconds @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g., 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,14 +112,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) """ default_subconverters = ( ps.FiniteB6BridgeConverter(), diff --git a/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py index 11062e99..1152d0d1 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py @@ -36,7 +36,7 @@ class FiniteTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir ``['omega' , 'torque', 'i_sd', 'i_sq', 'i_a', 'i_b', 'i_c', 'i_e', 'u_sd', 'u_sq', 'u_a', 'u_b', 'u_c', 'u_e', 'u_sup']`` Reference Variables: - ``['i_sd', 'i_sq', 'i_e']`` + ``['torque']`` Control Cycle Time: tau = 1e-5 seconds @@ -99,8 +99,8 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment constraints(iterable(str/Constraint)): All Constraints of the environment. \n - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) - can be directly specified by passing the state name here (e.g. 'i', 'omega') \n - - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + can be directly specified by passing the state name here (e.g., 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g., the SquaredConstraint can be initialized and passed to the environment. calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the simulation. This may lead to speed improvements. Default: True @@ -112,14 +112,14 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve All parameters of type env-arg can be selected as one of the following types: **instance:** Pass an already instantiated object derived from the corresponding base class - (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + (e.g., ``reward_function=MyRewardFunction()``). This is directly used in the environment. **dict:** Pass a dict to update the default parameters of the default type. - (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + (e.g., ``visualization=dict(state_plots=('omega', 'u'))``) **str:** Pass a string out of the registered classes to select a different class for the component. This class is then initialized with its default parameters. - The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + The available strings can be looked up in the documentation. (e.g., ``converter='Finite-2QC'``) """ default_subconverters = ( ps.FiniteB6BridgeConverter(), From 9dcc1b6d278fec9e7f3bf61fe55da91aeed5577f Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 11 Apr 2022 20:46:07 +0200 Subject: [PATCH 65/79] EESM Equations and Schematic --- .../physical_systems/electric_motors/eesm.rst | 19 +- docs/plots/ESB_EESM_dq.svg | 2941 +++++++++++++++++ .../envs/gym_eesm/cont_cc_eesm_env.py | 4 +- .../envs/gym_eesm/cont_sc_eesm_env.py | 2 +- .../envs/gym_eesm/cont_tc_eesm_env.py | 4 +- .../envs/gym_eesm/finite_tc_eesm_env.py | 4 +- 6 files changed, 2966 insertions(+), 8 deletions(-) create mode 100644 docs/plots/ESB_EESM_dq.svg diff --git a/docs/parts/physical_systems/electric_motors/eesm.rst b/docs/parts/physical_systems/electric_motors/eesm.rst index 700f63f6..8d5b6d37 100644 --- a/docs/parts/physical_systems/electric_motors/eesm.rst +++ b/docs/parts/physical_systems/electric_motors/eesm.rst @@ -3,20 +3,37 @@ Extertnally Excited Synchronous Motor Schematic ********* +.. figure:: ../../../plots/ESB_EESM_dq.svg +.. math:: + + L_{\sigma \mathrm{d}} &= L_\mathrm{d} - L_\mathrm{m} \\ + L_{\sigma \mathrm{e}} &= L_\mathrm{e} - L_\mathrm{m} \\ + \psi_\mathrm{d} &= L_\mathrm{d} i_\mathrm{sd} + L_\mathrm{m} i_\mathrm{e} \\ + \psi_\mathrm{q} &= L_\mathrm{q} i_\mathrm{sq} Electrical ODE ************** +.. math:: + \sigma = \frac{L_\mathrm{d} L_\mathrm{e} - L_\mathrm{m}^2}{L_\mathrm{d} L_\mathrm{e}} + +.. math:: + \frac{\mathrm{d} i_\mathrm{sd}}{\mathrm{d} t} &= - \frac{ R_\mathrm{s} }{ \sigma L_{\mathrm{d} } } i_\mathrm{sd} + \frac{L_\mathrm{q}}{\sigma L_\mathrm{d}} p \omega_\mathrm{me} i_\mathrm{sq} + \frac{L_\mathrm{m} R_\mathrm{e}}{\sigma L_\mathrm{d} L_\mathrm{e}} i_\mathrm{e} + \frac{1}{\sigma L_\mathrm{d}} u_\mathrm{d} - \frac{L_\mathrm{m}}{\sigma L_\mathrm{d} L_\mathrm{e}} u_\mathrm{e} \\ + \frac{\mathrm{d} i_\mathrm{sq}}{\mathrm{d} t} &= -\frac{L_\mathrm{d}}{L_\mathrm{q}} p \omega_\mathrm{me} i_\mathrm{sd} - \frac{R_\mathrm{s}}{L_\mathrm{q}} i_\mathrm{sq} - \frac{L_\mathrm{m}}{L_\mathrm{q}} p \omega_\mathrm{me} i_\mathrm{e} + \frac{1}{L_\mathrm{q}} u_\mathrm{q} \\ + \frac{\mathrm{d} i_\mathrm{e}}{\mathrm{d} t} &= \frac{L_\mathrm{m} R_\mathrm{s}}{\sigma L_\mathrm{d} L_\mathrm{e}} i_\mathrm{sd} - \frac{L_\mathrm{m} L_\mathrm{q}}{ \sigma L_\mathrm{d} L_\mathrm{e}} p \omega_\mathrm{me} i_\mathrm{sq} - \frac{R_\mathrm{s}}{ \sigma L_\mathrm{e}} i_\mathrm{e} - \frac{L_\mathrm{m}}{\sigma L_\mathrm{d}L_\mathrm{e}} u_\mathrm{d} + \frac{1}{\sigma L_\mathrm{e}} u_\mathrm{e} \\ + \frac{\mathrm{d} \varepsilon_\mathrm{el}}{\mathrm{d} t} &= p \omega_\mathrm{me} \\ Torque Equation *************** +.. math:: + T = 1.5 p (L_\mathrm{m} i_\mathrm{e} + (L_\mathrm{d} - L_\mathrm{q}) i_\mathrm{sd}) i_\mathrm{sq} Code Documentation ****************** -.. autoclass:: gym_electric_motor.physical_systems.electric_motors.PermanentMagnetSynchronousMotor +.. autoclass:: gym_electric_motor.physical_systems.electric_motors.ExternallyExcitedSynchronousMotor :members: :inherited-members: diff --git a/docs/plots/ESB_EESM_dq.svg b/docs/plots/ESB_EESM_dq.svg new file mode 100644 index 00000000..b73bf95c --- /dev/null +++ b/docs/plots/ESB_EESM_dq.svg @@ -0,0 +1,2941 @@ + +image/svg+xml20 + + +2 + + +Permanentmagnet + + +- + + +Synchronmotor + + +Permanent Magnet Synchronous Motor + + +die Fluss + + +- + + +durch die Stromableitungen, erhält + + +man + + +current derivatives in the above voltage + + +equations, it follows + + +p + + +sd + + +LLs + + +sq + + +LLs + + +sq + + +LLs + + +sq + + +sq + + +LLs + + +sd + + +LLs + + +sd + + +LLs + + +sd + + +i + + +L + + +i + + +L + + +i + + +R + + +u + + +i + + +L + + +i + + +L + + +i + + +R + + +u + + +ω + + +ωω + + +ωω + + +ωω + + +ωω + + +ωω + + +ωω + + +ωω + + + + + + + + + + + + + + +( + + +2 + + +. + + +35 + + +) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +( + + +2 + + +. + + +36 + + +) + + + + + + + + + + + + + + +Fig. + + +2 + + +- + + +8 + + +: + + +Ersatzschaltbild des Per + + +manent + + +- + + +magnet + + +- + + +Synchronmotors in + + +d/q + + +- + + +Koordinaten + + +Equivalent circuit diagram of the + + +permanent magnet synchronous + + +motor in + + +d/q + + +- + + +coordinates + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py index 0baecb00..c1e84c45 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_cc_eesm_env.py @@ -19,7 +19,7 @@ class ContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro Default Components: - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` + - Converter: :py:class:`.ContMultiConverter`(:py:class:`.ContB6BridgeConverter`, :py:class:`.ContFourQuadrantConverter`) - Motor: :py:class:`.ExternallyExcitedSynchronousMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.ScipyOdeSolver` @@ -51,7 +51,7 @@ class ContCurrentControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviro Box(low=[-1, -1, 0], high=[1, 1, 1]) Action Space: - Box(low=[-1, -1, -1], high=[1, 1, 1]) + Box(low=[-1, -1, -1, -1], high=[1, 1, 1, 1]) Initial State: Zeros on all state variables. diff --git a/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py index f3076818..65978038 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py @@ -19,7 +19,7 @@ class ContSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironm Default Components: - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` + - Converter: :py:class:`.ContMultiConverter`(:py:class:`.ContB6BridgeConverter`, :py:class:`.ContFourQuadrantConverter`) - Motor: :py:class:`.ExternallyExcitedSynchronousMotor` - Load: :py:class:`.PolynomialStaticLoad` - Ode-Solver: :py:class:`.ScipyOdeSolver` diff --git a/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py index af05575c..489723a5 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py @@ -15,11 +15,11 @@ class ContTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviron Environment to simulate a continuous control set torque controlled externally excited synchronous motor. Key: - ``'Cont-TC-PMSM-v0'`` + ``'Cont-TC-EESM-v0'`` Default Components: - Supply: :py:class:`.IdealVoltageSupply` - - Converter: :py:class:`.ContB6BridgeConverter` + - Converter: :py:class:`.ContMultiConverter`(:py:class:`.ContB6BridgeConverter`, :py:class:`.ContFourQuadrantConverter`) - Motor: :py:class:`.ExternallyExcitedSynchronousMotor` - Load: :py:class:`.ConstantSpeedLoad` - Ode-Solver: :py:class:`.EulerSolver` diff --git a/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py index 1152d0d1..1a3ea629 100644 --- a/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/finite_tc_eesm_env.py @@ -45,10 +45,10 @@ class FiniteTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvir Type: Tuple(State_Space, Reference_Space) State Space: - Box(low=15 * [-1], high=15 * [1]) + Box(low = 15 * [-1], high = 15 * [1]) Reference Space: - Box(low=[-1, -1, -1], high=[1, 1, 1]) + Box(low=[-1], high=[1]) Action Space: MultiDiscrete((8, 4)) From ea4b1bb358ddc0b577b3a832ce9bdfa8618cb152 Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 11 Apr 2022 20:58:39 +0200 Subject: [PATCH 66/79] Doc fix --- gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py index 489723a5..c23f6a4f 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_tc_eesm_env.py @@ -49,7 +49,7 @@ class ContTorqueControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnviron Box(low=15 * [-1], high=15 * [1]) Reference Space: - Box(low=[-1, -1], high=[1, 1]) + Box(low=[-1], high=[1]) Action Space: Box(low=[-1, -1, -1, -1], high=[1, 1, 1, 1]) From ff0f3b13742a34ea081302e82cd8ca6d74785b1d Mon Sep 17 00:00:00 2001 From: Arne Date: Wed, 20 Apr 2022 17:17:34 +0200 Subject: [PATCH 67/79] comment fixes --- gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py b/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py index 65978038..f6e94668 100644 --- a/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py +++ b/gym_electric_motor/envs/gym_eesm/cont_sc_eesm_env.py @@ -48,7 +48,7 @@ class ContSpeedControlExternallyExcitedSynchronousMotorEnv(ElectricMotorEnvironm Box(low=15 * [-1], high=15 * [1]) Reference Space: - Box(low=[-1, -1], high=[1, 1]) + Box(low=[-1], high=[1]) Action Space: Box(low=[-1, -1, -1, -1], high=[1, 1, 1, 1]) From 2d2da80263fc744c6556b48b4a222f1205e22f82 Mon Sep 17 00:00:00 2001 From: Arne Date: Thu, 21 Apr 2022 13:38:31 +0200 Subject: [PATCH 68/79] First version of changelog for the next release --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 498bcbe2..8a1b98c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ## Added - State Action processors as a new feature to process actions and states directly in the environment. +- The externally excited synchronous motor (EESM) has been added to the GEM-toolbox. + +## Changed +- The MotorDashboard has received a "initialize()" method to initialize the plots below a specific cell. +- The MotorDashboard is now compatible with the "%matplotlib widget" backend. Therefore, GEM is now compatible with the integrated jupiter notebook execution of Visual Studio Code + +## Fixed +- If multiple converters were used and the time constant tau was changed from its default values, it was possible that the values of tau were different in each converter + ## [1.0.1] - 2021-12-20 ## Added From e553da33157a1a072e3a450cdf264c4daed98050 Mon Sep 17 00:00:00 2001 From: Arne Date: Fri, 22 Apr 2022 17:18:56 +0200 Subject: [PATCH 69/79] Version bump in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2a6c2dfb..12b374b5 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setuptools.setup( name='gym_electric_motor', - version='1.0.0', + version='1.1.0', description='An OpenAI gym environment for electric motor control.', packages=setuptools.find_packages(), install_requires=requirements, From 66fcbca0a52196b8b3bd44c26a6e82f7500a8b8b Mon Sep 17 00:00:00 2001 From: Arne Date: Fri, 22 Apr 2022 17:22:58 +0200 Subject: [PATCH 70/79] Versioning in changelog.md --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a1b98c2..5b729a3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +##[Unreleased] +## Added + +## Changed + +## Fixed + +## [1.1.0] - 2022-04-25 ## Added - State Action processors as a new feature to process actions and states directly in the environment. - The externally excited synchronous motor (EESM) has been added to the GEM-toolbox. From 7c2e31304c54c20451bd8f9f9f032f8fa6407111 Mon Sep 17 00:00:00 2001 From: Arne Date: Wed, 11 May 2022 20:25:14 +0200 Subject: [PATCH 71/79] Docstring on unwrapped property of physical system --- gym_electric_motor/core.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gym_electric_motor/core.py b/gym_electric_motor/core.py index 14902be3..3dfc39c9 100644 --- a/gym_electric_motor/core.py +++ b/gym_electric_motor/core.py @@ -504,6 +504,9 @@ class PhysicalSystem: @property def unwrapped(self): + """Returns this instance of the physical system. + + If the system is wrapped into multiple StateActionProcessors this property returns directly the innermost system.""" return self @property From 25eca85907ed895bd080fefa12e9b5d69f0df01b Mon Sep 17 00:00:00 2001 From: Arne Date: Wed, 11 May 2022 20:29:09 +0200 Subject: [PATCH 72/79] Added args to docstring of set_switching_pattern --- gym_electric_motor/physical_systems/converters.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gym_electric_motor/physical_systems/converters.py b/gym_electric_motor/physical_systems/converters.py index 23d44a3e..d33b9959 100644 --- a/gym_electric_motor/physical_systems/converters.py +++ b/gym_electric_motor/physical_systems/converters.py @@ -103,6 +103,9 @@ def _set_switching_pattern(self, action): Method to calculate the switching pattern and corresponding switching times for the next time step. At least, the next time step [t + tau] is returned. + Args: + action(instance of action_space): The action for the next time step. + Returns: list(float): Switching times. """ From 998c324bff2d3f80c6db49274f6f8f8734e94763 Mon Sep 17 00:00:00 2001 From: Arne Date: Wed, 11 May 2022 20:31:56 +0200 Subject: [PATCH 73/79] Added motor parameter source in docstring --- .../physical_systems/electric_motors/dc_motor.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gym_electric_motor/physical_systems/electric_motors/dc_motor.py b/gym_electric_motor/physical_systems/electric_motors/dc_motor.py index 728c0b43..ca853092 100644 --- a/gym_electric_motor/physical_systems/electric_motors/dc_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/dc_motor.py @@ -10,6 +10,7 @@ class DcMotor(ElectricMotor): This includes the system equations, the motor parameters of the equivalent circuit diagram, as well as limits. + ===================== ========== ============= =========================================== Motor Parameter Unit Default Value Description ===================== ========== ============= =========================================== @@ -20,6 +21,10 @@ class DcMotor(ElectricMotor): l_e_prime H 0.0094 Effective excitation inductance j_rotor kg/m^2 0.017 Moment of inertia of the rotor ===================== ========== ============= =========================================== + + ..note :: + The motor parameter are based on the following DC Motor (slightly adapted): + https://www.heinzmann-electric-motors.com/en/products/dc-motors/pmg-132-dc-motor =============== ====== ============================================= Motor Currents Unit Description From d4d401af02c0959e9af1f658abe84c1b324f68ef Mon Sep 17 00:00:00 2001 From: Arne Date: Wed, 11 May 2022 20:40:00 +0200 Subject: [PATCH 74/79] Improved changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b729a3e..23e25289 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,8 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.1.0] - 2022-04-25 ## Added -- State Action processors as a new feature to process actions and states directly in the environment. -- The externally excited synchronous motor (EESM) has been added to the GEM-toolbox. +- State Action processors as a new feature to process actions and states directly in the environment. A decent introduction can be found in the [GEM cookbook](https://github.com/upb-lea/gym-electric-motor/blob/nightly/examples/environment_features/GEM_cookbook.ipynb) (Paragraph 2.8) +- The externally excited synchronous motor (EESM) has been added to the GEM-toolbox. +- The environments of the EESM can be instantiated with the following keys: "{Cont|Finite}-{CC|TC|SC}-EESM-v0", ## Changed - The MotorDashboard has received a "initialize()" method to initialize the plots below a specific cell. From afa5094458e9f7dcd698f7333118dc30016274c3 Mon Sep 17 00:00:00 2001 From: Felix Book <64723297+fbook98@users.noreply.github.com> Date: Mon, 23 May 2022 17:14:46 +0200 Subject: [PATCH 75/79] Update motor parameter --- .../externally_excited_synchronous_motor.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py index ca3f4355..433b83c8 100644 --- a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py @@ -11,9 +11,10 @@ class ExternallyExcitedSynchronousMotor(SynchronousMotor): ===================== ========== ============= =========================================== r_s mOhm 15.55 Stator resistance r_e mOhm 7.2 Excitation resistance - l_d mH 0.166 Direct axis inductance + l_d mH 1.66 Direct axis inductance l_q mH 0.35 Quadrature axis inductance - l_e mH 1.66 Excitation inductance + l_m mH 1.589 Mutual inductance + l_e mH 1.74 Excitation inductance p 1 3 Pole pair number j_rotor kg/m^2 0.3883 Moment of inertia of the rotor ===================== ========== ============= =========================================== @@ -98,8 +99,8 @@ class ExternallyExcitedSynchronousMotor(SynchronousMotor): 'p': 3, 'l_d': 1.66e-3, 'l_q': 0.35e-3, - 'l_m': 1.66e-4, - 'l_e': 1.66e-3, + 'l_m': 1.589e-3, + 'l_e': 1.74e-3, 'j_rotor': 0.3883, 'r_s': 15.55e-3, 'r_e': 7.2e-3, From 1d7f54f8be8df0c86d47e5761ef7f08192112b0c Mon Sep 17 00:00:00 2001 From: Arne Date: Mon, 23 May 2022 21:21:35 +0200 Subject: [PATCH 76/79] Renaming of StateActionProcessor to PS-Wrapper. --- CHANGELOG.md | 2 +- docs/index.rst | 2 +- .../cos_sin_processor.rst | 2 +- .../current_sum_processor.rst | 2 +- .../dead_time_processor.rst | 2 +- .../dq_to_abc_action_processor.rst | 2 +- .../state_action_processors/flux_observer.rst | 2 +- .../state_action_processor.rst | 8 +++--- .../state_noise_processor.rst | 2 +- .../classic_controllers_ind_motor_example.py | 4 +-- .../environment_features/GEM_cookbook.ipynb | 26 +++++++++---------- .../ddpg_pmsm_dq_current_control.py | 6 ++--- gym_electric_motor/__init__.py | 2 +- gym_electric_motor/core.py | 10 +++---- .../cont_cc_extex_dc_env.py | 6 ++--- .../cont_sc_extex_dc_env.py | 6 ++--- .../cont_tc_extex_dc_env.py | 6 ++--- .../finite_cc_extex_dc_env.py | 6 ++--- .../finite_sc_extex_dc_env.py | 6 ++--- .../finite_tc_extex_dc_env.py | 6 ++--- .../cont_cc_permex_dc_env.py | 6 ++--- .../cont_sc_permex_dc_env.py | 6 ++--- .../cont_tc_permex_dc_env.py | 6 ++--- .../finite_cc_permex_dc_env.py | 6 ++--- .../finite_sc_permex_dc_env.py | 6 ++--- .../finite_tc_permex_dc_env.py | 6 ++--- .../cont_cc_series_dc_env.py | 6 ++--- .../cont_sc_series_dc_env.py | 6 ++--- .../cont_tc_series_dc_env.py | 6 ++--- .../finite_cc_series_dc_env.py | 6 ++--- .../finite_sc_series_dc_env.py | 6 ++--- .../finite_tc_series_dc_env.py | 6 ++--- .../cont_cc_shunt_dc_env.py | 8 +++--- .../cont_sc_shunt_dc_env.py | 8 +++--- .../cont_tc_shunt_dc_env.py | 8 +++--- .../finite_cc_shunt_dc_env.py | 8 +++--- .../finite_sc_shunt_dc_env.py | 8 +++--- .../finite_tc_shunt_dc_env.py | 8 +++--- .../cont_cc_dfim_env.py | 6 ++--- .../cont_sc_dfim_env.py | 6 ++--- .../cont_tc_dfim_env.py | 6 ++--- .../finite_cc_dfim_env.py | 6 ++--- .../finite_sc_dfim_env.py | 6 ++--- .../finite_tc_dfim_env.py | 6 ++--- .../cont_cc_scim_env.py | 6 ++--- .../cont_sc_scim_env.py | 6 ++--- .../cont_tc_scim_env.py | 6 ++--- .../finite_cc_scim_env.py | 6 ++--- .../finite_sc_scim_env.py | 6 ++--- .../finite_tc_scim_env.py | 6 ++--- .../envs/gym_pmsm/cont_cc_pmsm_env.py | 6 ++--- .../envs/gym_pmsm/cont_sc_pmsm_env.py | 6 ++--- .../envs/gym_pmsm/cont_tc_pmsm_env.py | 6 ++--- .../envs/gym_pmsm/finite_cc_pmsm_env.py | 6 ++--- .../envs/gym_pmsm/finite_sc_pmsm_env.py | 6 ++--- .../envs/gym_pmsm/finite_tc_pmsm_env.py | 6 ++--- .../envs/gym_synrm/cont_cc_synrm_env.py | 6 ++--- .../envs/gym_synrm/cont_sc_synrm_env.py | 6 ++--- .../envs/gym_synrm/cont_tc_synrm_env.py | 6 ++--- .../envs/gym_synrm/finite_cc_synrm_env.py | 6 ++--- .../envs/gym_synrm/finite_sc_synrm_env.py | 6 ++--- .../envs/gym_synrm/finite_tc_synrm_env.py | 6 ++--- .../__init__.py | 2 +- .../cos_sin_processor.py | 4 +-- .../current_sum_processor.py | 4 +-- .../dead_time_processor.py | 6 ++--- .../dq_to_abc_action_processor.py | 4 +-- .../flux_observer.py | 4 +-- .../physical_system_wrapper.py} | 14 +++++----- .../state_noise_processor.py | 8 +++--- .../externally_excited_synchronous_motor.py | 8 ++++-- .../__init__.py | 0 .../test_cos_sin_processor.py | 6 ++--- .../test_dead_time_processor.py | 18 ++++++------- .../test_dq_to_abc_action_processor.py | 6 ++--- .../test_flux_observer.py | 8 +++--- .../test_physical_system_wrapper.py} | 6 ++--- tests/utils/__init__.py | 2 +- ...sor.py => physical_system_test_wrapper.py} | 2 +- 79 files changed, 239 insertions(+), 235 deletions(-) rename gym_electric_motor/{state_action_processors => physical_system_wrappers}/__init__.py (84%) rename gym_electric_motor/{state_action_processors => physical_system_wrappers}/cos_sin_processor.py (96%) rename gym_electric_motor/{state_action_processors => physical_system_wrappers}/current_sum_processor.py (95%) rename gym_electric_motor/{state_action_processors => physical_system_wrappers}/dead_time_processor.py (95%) rename gym_electric_motor/{state_action_processors => physical_system_wrappers}/dq_to_abc_action_processor.py (97%) rename gym_electric_motor/{state_action_processors => physical_system_wrappers}/flux_observer.py (97%) rename gym_electric_motor/{state_action_processors/state_action_processor.py => physical_system_wrappers/physical_system_wrapper.py} (87%) rename gym_electric_motor/{state_action_processors => physical_system_wrappers}/state_noise_processor.py (92%) rename tests/{test_state_action_processors => test_physical_system_wrappers}/__init__.py (100%) rename tests/{test_state_action_processors => test_physical_system_wrappers}/test_cos_sin_processor.py (85%) rename tests/{test_state_action_processors => test_physical_system_wrappers}/test_dead_time_processor.py (78%) rename tests/{test_state_action_processors => test_physical_system_wrappers}/test_dq_to_abc_action_processor.py (83%) rename tests/{test_state_action_processors => test_physical_system_wrappers}/test_flux_observer.py (88%) rename tests/{test_state_action_processors/test_state_action_processor.py => test_physical_system_wrappers/test_physical_system_wrapper.py} (87%) rename tests/utils/{state_action_test_processor.py => physical_system_test_wrapper.py} (82%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23e25289..ec21a9ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.1.0] - 2022-04-25 ## Added -- State Action processors as a new feature to process actions and states directly in the environment. A decent introduction can be found in the [GEM cookbook](https://github.com/upb-lea/gym-electric-motor/blob/nightly/examples/environment_features/GEM_cookbook.ipynb) (Paragraph 2.8) +- Physical System Wrappers as a new feature to process actions and states directly in the environment. A decent introduction can be found in the [GEM cookbook](https://github.com/upb-lea/gym-electric-motor/blob/nightly/examples/environment_features/GEM_cookbook.ipynb) (Paragraph 2.8) - The externally excited synchronous motor (EESM) has been added to the GEM-toolbox. - The environments of the EESM can be instantiated with the following keys: "{Cont|Finite}-{CC|TC|SC}-EESM-v0", diff --git a/docs/index.rst b/docs/index.rst index 3bf427eb..0e12c7b0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -43,7 +43,7 @@ documentation specifies the basic interfaces inside a physical system. parts/reference_generators/reference_generator parts/reward_functions/reward_function parts/physical_systems/physical_system - parts/state_action_processors/state_action_processor + parts/physical_system_wrappers/physical_system_wrapper parts/visualizations/visualization parts/constraint_monitor parts/core diff --git a/docs/parts/state_action_processors/cos_sin_processor.rst b/docs/parts/state_action_processors/cos_sin_processor.rst index 09dd01c8..f3caa44a 100644 --- a/docs/parts/state_action_processors/cos_sin_processor.rst +++ b/docs/parts/state_action_processors/cos_sin_processor.rst @@ -1,6 +1,6 @@ Cos Sin Processor ##################### -.. autoclass:: gym_electric_motor.state_action_processors.CosSinProcessor +.. autoclass:: gym_electric_motor.physical_system_wrappers.CosSinProcessor :members: :inherited-members: diff --git a/docs/parts/state_action_processors/current_sum_processor.rst b/docs/parts/state_action_processors/current_sum_processor.rst index 107ac5ac..39e12c12 100644 --- a/docs/parts/state_action_processors/current_sum_processor.rst +++ b/docs/parts/state_action_processors/current_sum_processor.rst @@ -1,6 +1,6 @@ Current Sum Processor ###################### -.. autoclass:: gym_electric_motor.state_action_processors.current_sum_processor.CurrentSumProcessor +.. autoclass:: gym_electric_motor.physical_system_wrappers.current_sum_processor.CurrentSumProcessor :members: :inherited-members: diff --git a/docs/parts/state_action_processors/dead_time_processor.rst b/docs/parts/state_action_processors/dead_time_processor.rst index 978d8533..f9de4e2d 100644 --- a/docs/parts/state_action_processors/dead_time_processor.rst +++ b/docs/parts/state_action_processors/dead_time_processor.rst @@ -1,6 +1,6 @@ Dead Time Processor ##################### -.. autoclass:: gym_electric_motor.state_action_processors.DeadTimeProcessor +.. autoclass:: gym_electric_motor.physical_system_wrappers.DeadTimeProcessor :members: :inherited-members: diff --git a/docs/parts/state_action_processors/dq_to_abc_action_processor.rst b/docs/parts/state_action_processors/dq_to_abc_action_processor.rst index 5b37a066..0ad8d476 100644 --- a/docs/parts/state_action_processors/dq_to_abc_action_processor.rst +++ b/docs/parts/state_action_processors/dq_to_abc_action_processor.rst @@ -1,7 +1,7 @@ Dq To Abc Action Processor ########################## -.. autoclass:: gym_electric_motor.state_action_processors.DqToAbcActionProcessor +.. autoclass:: gym_electric_motor.physical_system_wrappers.DqToAbcActionProcessor :members: :inherited-members: diff --git a/docs/parts/state_action_processors/flux_observer.rst b/docs/parts/state_action_processors/flux_observer.rst index 0562af07..4e264c8f 100644 --- a/docs/parts/state_action_processors/flux_observer.rst +++ b/docs/parts/state_action_processors/flux_observer.rst @@ -1,6 +1,6 @@ Flux Observer ###################### -.. autoclass:: gym_electric_motor.state_action_processors.FluxObserver +.. autoclass:: gym_electric_motor.physical_system_wrappers.FluxObserver :members: :inherited-members: diff --git a/docs/parts/state_action_processors/state_action_processor.rst b/docs/parts/state_action_processors/state_action_processor.rst index 123718c9..4664cec8 100644 --- a/docs/parts/state_action_processors/state_action_processor.rst +++ b/docs/parts/state_action_processors/state_action_processor.rst @@ -1,9 +1,9 @@ -State Action Processor +Physical System Wrapper ####################### .. toctree:: :maxdepth: 1 - :caption: Available StateActionProcessors: + :caption: Available PhysicalSystemWrappers: current_sum_processor flux_observer @@ -13,8 +13,8 @@ State Action Processor state_noise_processor -State Action Processor Base Class +Physical System Wrapper Base Class '''''''''''''''''''''''''''''''''' -.. autoclass:: gym_electric_motor.state_action_processors.StateActionProcessor +.. autoclass:: gym_electric_motor.physical_system_wrappers.PhysicalSystemWrapper :members: diff --git a/docs/parts/state_action_processors/state_noise_processor.rst b/docs/parts/state_action_processors/state_noise_processor.rst index ed38a541..b49de71c 100644 --- a/docs/parts/state_action_processors/state_noise_processor.rst +++ b/docs/parts/state_action_processors/state_noise_processor.rst @@ -1,6 +1,6 @@ State Noise Processor ###################### -.. autoclass:: gym_electric_motor.state_action_processors.StateNoiseProcessor +.. autoclass:: gym_electric_motor.physical_system_wrappers.StateNoiseProcessor :members: :inherited-members: diff --git a/examples/classic_controllers/classic_controllers_ind_motor_example.py b/examples/classic_controllers/classic_controllers_ind_motor_example.py index 04803219..3908f10d 100644 --- a/examples/classic_controllers/classic_controllers_ind_motor_example.py +++ b/examples/classic_controllers/classic_controllers_ind_motor_example.py @@ -3,7 +3,7 @@ from external_plot import ExternalPlot import gym_electric_motor as gem from gym_electric_motor.visualization import MotorDashboard -from gym_electric_motor.state_action_processors import FluxObserver +from gym_electric_motor.physical_system_wrappers import FluxObserver import numpy as np if __name__ == '__main__': @@ -31,7 +31,7 @@ external_ref_plots += external_plot # initialize the gym-electric-motor environment - env = gem.make(env_id, state_action_processors=(FluxObserver(),), + env = gem.make(env_id, physical_system_wrappers=(FluxObserver(),), visualization=MotorDashboard(state_plots=('omega', 'psi_abs', 'psi_angle'))) """ diff --git a/examples/environment_features/GEM_cookbook.ipynb b/examples/environment_features/GEM_cookbook.ipynb index 23dee129..05ee8cd5 100644 --- a/examples/environment_features/GEM_cookbook.ipynb +++ b/examples/environment_features/GEM_cookbook.ipynb @@ -283,7 +283,7 @@ "* tau(float)\n", "* state_filter(list(str))\n", "* callbacks(list(Callback))\n", - "* state_action_processors(iterable(StateActionProcessor))\n", + "* physical_system_wrappers(iterable(PhysicalSystemWrapper))\n", "\n", "#### Environment-arg type:\n", "The `env-arg` type is a short notation for all parameters that can be passed in three different ways to override the default behavior. Exemplary, it is shown how to change the reference generator of an environment in three different ways:\n", @@ -685,14 +685,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### 2.8 State Action Processors\n", - "State Action Processors wrap the physical system to preprocess the actions and postprocess the states of the Physical Systems.\n", + "### 2.8 Physical System Wrappers\n", + "Physical System Wrappers wrap the physical system to preprocess the actions and postprocess the states of the Physical Systems.\n", "\n", - "They are very similar to gym-wrappers, which are put around a gym-environment and process actions observations and rewards. The big difference between a gym-wrapper and a state action processor is that the state action processor is wrapped around the physical system and not around the whole environment.\n", + "They are very similar to gym-wrappers, which are put around a gym-environment and process actions observations and rewards. The big difference between a gym-wrapper and a Physical System Wrapper is that the Physical System Wrapper is wrapped around the physical system and not around the whole environment.\n", "\n", "The ``CosSinProcessor`` in the following example adds the cosine and sine of the rotor angle ``epsilon`` to the state vector. These states can directly influence the calculated reward, or referenced by the reference generator and visualized by the dashboard (c.f. 2.6). \n", "\n", - "The state action processors are passed as iterable with the argument ``state_action_processors`` as iterable (list/tuple) to the environment during the make-call (c.f. 2.9). The state action processors can also be stacked into each other. The first processor in the list is wrapped around the physical system at first and the latter ones are put around the resulting system one after the other. Therefore, outer state action processors access the processed states from the inner ones." + "The Physical System Wrappers are passed as iterable with the argument ``physical_system_wrappers`` as iterable (list/tuple) to the environment during the make-call (c.f. 2.9). The Physical System Wrappers can also be stacked into each other. The first processor in the list is wrapped around the physical system at first and the latter ones are put around the resulting system one after the other. Therefore, outer Physical System Wrappers access the processed states from the inner ones." ] }, { @@ -701,10 +701,10 @@ "metadata": {}, "outputs": [], "source": [ - "from gym_electric_motor.state_action_processors import CosSinProcessor, StateNoiseProcessor\n", + "from gym_electric_motor.physical_system_wrappers import CosSinProcessor, StateNoiseProcessor\n", "\n", "# \n", - "state_action_processors = [\n", + "physical_system_wrappers = [\n", " # Wrapped directly around the physical system\n", " CosSinProcessor(angle='epsilon'),\n", " # Wrapped around the CosSinProcessor. Therefore, the generated states (cos and sin) can be accessed.\n", @@ -720,8 +720,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### 2.8.1 Custom State Action Processor\n", - "A custom state action processor has to inherit from the base ``StateActionProcessor`` class. \n", + "### 2.8.1 Custom Physical System Wrapper\n", + "A custom Physical System Wrapper has to inherit from the base ``PhysicalSystemWrapper`` class. \n", "\n", "In the next cell an example of a user-defined processor is shown. \n", "\n", @@ -741,10 +741,10 @@ "import gym\n", "import numpy as np\n", "\n", - "from gym_electric_motor.state_action_processors import StateActionProcessor\n", + "from gym_electric_motor.physical_system_wrappers import PhysicalSystemWrapper\n", "\n", "\n", - "class CurrentVectorProcessor(StateActionProcessor):\n", + "class CurrentVectorProcessor(PhysicalSystemWrapper):\n", " \"\"\"Adds an ``i_abs`` state to the systems state vector that is the root of the squared sum of the currents ``i_sd`` and `` i_sq``.\"\"\"\n", "\n", " def __init__(self, physical_system=None):\n", @@ -813,7 +813,7 @@ "metadata": {}, "outputs": [], "source": [ - "state_action_processors.append(CurrentVectorProcessor())" + "physical_system_wrappers.append(CurrentVectorProcessor())" ] }, { @@ -855,7 +855,7 @@ " reference_generator=rg,\n", " ode_solver='euler',\n", " callbacks=my_callback,\n", - " state_action_processors=state_action_processors, # Pass the state action processors\n", + " physical_system_wrappers=physical_system_wrappers, # Pass the Physical System Wrappers\n", " constraints=constraints\n", ")" ] diff --git a/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py b/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py index fe310e08..7fc41442 100644 --- a/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py +++ b/examples/reinforcement_learning_controllers/ddpg_pmsm_dq_current_control.py @@ -20,7 +20,7 @@ from gym.core import Wrapper from gym.spaces import Box, Tuple from gym_electric_motor.constraints import SquaredConstraint -from gym_electric_motor.state_action_processors import DqToAbcActionProcessor, DeadTimeProcessor +from gym_electric_motor.physical_system_wrappers import DqToAbcActionProcessor, DeadTimeProcessor ''' This example shows how we can use GEM to train a reinforcement learning agent to control the current within @@ -97,7 +97,7 @@ def reset(self, **kwargs): # Change the motor nominal values nominal_values = {key: 0.7 * limit for key, limit in limit_values.items()} - state_action_processors = ( + physical_system_wrappers = ( DeadTimeProcessor(), DqToAbcActionProcessor.make('PMSM'), ) @@ -107,7 +107,7 @@ def reset(self, **kwargs): # Choose the permanent magnet synchronous motor with continuous-control-set 'Cont-CC-PMSM-v0', # Pass a class with extra parameters - state_action_processors=state_action_processors, + physical_system_wrappers=physical_system_wrappers, visualization=MotorDashboard( state_plots=['i_sq', 'i_sd'], action_plots='all', diff --git a/gym_electric_motor/__init__.py b/gym_electric_motor/__init__.py index c74d2e85..0f9e3b70 100644 --- a/gym_electric_motor/__init__.py +++ b/gym_electric_motor/__init__.py @@ -17,7 +17,7 @@ import gym_electric_motor.visualization import gym_electric_motor.physical_systems import gym_electric_motor.envs -import gym_electric_motor.state_action_processors +import gym_electric_motor.physical_system_wrappers from gym.envs.registration import register import gym diff --git a/gym_electric_motor/core.py b/gym_electric_motor/core.py index 3dfc39c9..bf0752ad 100644 --- a/gym_electric_motor/core.py +++ b/gym_electric_motor/core.py @@ -165,7 +165,7 @@ def visualizations(self): return self._visualizations def __init__(self, physical_system, reference_generator, reward_function, visualization=(), state_filter=None, - callbacks=(), constraints=(), state_action_processors=(), **kwargs): + callbacks=(), constraints=(), physical_system_wrappers=(), **kwargs): """ Setting and initialization of all environments' modules. @@ -184,7 +184,7 @@ def __init__(self, physical_system, reference_generator, reward_function, visual ConstraintMonitor in the environment. visualization(iterable(ElectricMotorVisualization)/None): The visualizations of this environment. state_filter(list(str)): Selection of states that are shown in the observation. - state_action_processors(iterable(StateActionProcessor)): StateActionProcessor instances to be wrapped around + physical_system_wrappers(iterable(PhysicalSystemWrapper)): PhysicalSystemWrapper instances to be wrapped around the physical system. callbacks(list(Callback)): Callbacks being called in the environment **kwargs: Arguments to be passed to the modules. @@ -207,8 +207,8 @@ def __init__(self, physical_system, reference_generator, reward_function, visual self._constraint_monitor = cm # Announcement of the modules among each other - for state_action_processor in state_action_processors: - self._physical_system = state_action_processor.set_physical_system(self._physical_system) + for physical_system_wrapper in physical_system_wrappers: + self._physical_system = physical_system_wrapper.set_physical_system(self._physical_system) self._reference_generator.set_modules(self.physical_system) self._constraint_monitor.set_modules(self.physical_system) self._reward_function.set_modules(self.physical_system, self._reference_generator, self._constraint_monitor) @@ -506,7 +506,7 @@ class PhysicalSystem: def unwrapped(self): """Returns this instance of the physical system. - If the system is wrapped into multiple StateActionProcessors this property returns directly the innermost system.""" + If the system is wrapped into multiple PhysicalSystemWrappers this property returns directly the innermost system.""" return self @property diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_cc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_cc_extex_dc_env.py index 9340a0cc..5a5cc252 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_cc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_cc_extex_dc_env.py @@ -85,7 +85,7 @@ class ContCurrentControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -106,7 +106,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -156,5 +156,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_sc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_sc_extex_dc_env.py index c154385f..59ff3741 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_sc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_sc_extex_dc_env.py @@ -85,7 +85,7 @@ class ContSpeedControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -106,7 +106,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -149,5 +149,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_tc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_tc_extex_dc_env.py index 0c072893..583191d2 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_tc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/cont_tc_extex_dc_env.py @@ -85,7 +85,7 @@ class ContTorqueControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e',), calc_jacobian=True, tau=1e-4, state_action_processors=()): + constraints=('i_a', 'i_e',), calc_jacobian=True, tau=1e-4, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -106,7 +106,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -149,5 +149,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_cc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_cc_extex_dc_env.py index 76905787..0b3777cf 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_cc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_cc_extex_dc_env.py @@ -85,7 +85,7 @@ class FiniteCurrentControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -106,7 +106,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -153,5 +153,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_sc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_sc_extex_dc_env.py index 52f88080..321a28d9 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_sc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_sc_extex_dc_env.py @@ -85,7 +85,7 @@ class FiniteSpeedControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -106,7 +106,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -150,5 +150,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_tc_extex_dc_env.py b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_tc_extex_dc_env.py index 1c078598..bbcba120 100644 --- a/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_tc_extex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/extex_dc_motor_env/finite_tc_extex_dc_env.py @@ -85,7 +85,7 @@ class FiniteTorqueControlDcExternallyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5,state_action_processors=()): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5,physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -106,7 +106,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -148,5 +148,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_cc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_cc_permex_dc_env.py index fe52b54c..a9c38fb5 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_cc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_cc_permex_dc_env.py @@ -86,7 +86,7 @@ class ContCurrentControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): def __init__( self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=() + constraints=('i',), calc_jacobian=True, tau=1e-4, physical_system_wrappers=() ): """ Args: @@ -108,7 +108,7 @@ def __init__( tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -146,5 +146,5 @@ def __init__( super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_sc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_sc_permex_dc_env.py index 00901f31..70a4da74 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_sc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_sc_permex_dc_env.py @@ -85,7 +85,7 @@ class ContSpeedControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): + constraints=('i',), calc_jacobian=True, tau=1e-4, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -106,7 +106,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -145,5 +145,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_tc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_tc_permex_dc_env.py index 6d539df0..7cc7d36e 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_tc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/cont_tc_permex_dc_env.py @@ -86,7 +86,7 @@ class ContTorqueControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): + constraints=('i',), calc_jacobian=True, tau=1e-4, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -107,7 +107,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -148,5 +148,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_cc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_cc_permex_dc_env.py index 8cc53c68..c2f8eba7 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_cc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_cc_permex_dc_env.py @@ -86,7 +86,7 @@ class FiniteCurrentControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment) def __init__( self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=() + constraints=('i',), calc_jacobian=True, tau=1e-5, physical_system_wrappers=() ): """ Args: @@ -108,7 +108,7 @@ def __init__( tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -145,5 +145,5 @@ def __init__( super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_sc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_sc_permex_dc_env.py index c40bee14..8abde974 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_sc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_sc_permex_dc_env.py @@ -86,7 +86,7 @@ class FiniteSpeedControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): def __init__( self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=() + constraints=('i',), calc_jacobian=True, tau=1e-5, physical_system_wrappers=() ): """ Args: @@ -108,7 +108,7 @@ def __init__( tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -148,5 +148,5 @@ def __init__( super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_tc_permex_dc_env.py b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_tc_permex_dc_env.py index 0a543794..21207b01 100644 --- a/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_tc_permex_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/permex_dc_motor_env/finite_tc_permex_dc_env.py @@ -86,7 +86,7 @@ class FiniteTorqueControlDcPermanentlyExcitedMotorEnv(ElectricMotorEnvironment): def __init__( self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=() + constraints=('i',), calc_jacobian=True, tau=1e-5, physical_system_wrappers=() ): """ Args: @@ -108,7 +108,7 @@ def __init__( tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -145,6 +145,6 @@ def __init__( super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_cc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_cc_series_dc_env.py index 9f038f7d..016e1878 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_cc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_cc_series_dc_env.py @@ -85,7 +85,7 @@ class ContCurrentControlDcSeriesMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): + constraints=('i',), calc_jacobian=True, tau=1e-4, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -107,7 +107,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -144,5 +144,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_sc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_sc_series_dc_env.py index 25717082..46d99492 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_sc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_sc_series_dc_env.py @@ -86,7 +86,7 @@ class ContSpeedControlDcSeriesMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): + constraints=('i',), calc_jacobian=True, tau=1e-4, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -107,7 +107,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -146,6 +146,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_tc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_tc_series_dc_env.py index 16789c50..432f737f 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_tc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/cont_tc_series_dc_env.py @@ -86,7 +86,7 @@ class ContTorqueControlDcSeriesMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-4, state_action_processors=()): + constraints=('i',), calc_jacobian=True, tau=1e-4, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -107,7 +107,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -143,5 +143,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_cc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_cc_series_dc_env.py index 8deb9dfe..c1cf1bd2 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_cc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_cc_series_dc_env.py @@ -85,7 +85,7 @@ class FiniteCurrentControlDcSeriesMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=()): + constraints=('i',), calc_jacobian=True, tau=1e-5, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -106,7 +106,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -142,5 +142,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_sc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_sc_series_dc_env.py index 0928d104..01e8368b 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_sc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_sc_series_dc_env.py @@ -85,7 +85,7 @@ class FiniteSpeedControlDcSeriesMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=()): + constraints=('i',), calc_jacobian=True, tau=1e-5, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -106,7 +106,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -145,6 +145,6 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_tc_series_dc_env.py b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_tc_series_dc_env.py index 43c4b3d2..6a1e6f3b 100644 --- a/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_tc_series_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/series_dc_motor_env/finite_tc_series_dc_env.py @@ -85,7 +85,7 @@ class FiniteTorqueControlDcSeriesMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i',), calc_jacobian=True, tau=1e-5, state_action_processors=()): + constraints=('i',), calc_jacobian=True, tau=1e-5, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -106,7 +106,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -144,5 +144,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py index 8a25a1c9..85a4d1e0 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_cc_shunt_dc_env.py @@ -6,7 +6,7 @@ from gym_electric_motor import physical_systems as ps from gym_electric_motor.reward_functions import WeightedSumOfErrors from gym_electric_motor.utils import initialize -from gym_electric_motor.state_action_processors import CurrentSumProcessor +from gym_electric_motor.physical_system_wrappers import CurrentSumProcessor class ContCurrentControlDcShuntMotorEnv(ElectricMotorEnvironment): @@ -87,7 +87,7 @@ class ContCurrentControlDcShuntMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -147,5 +147,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=tuple(state_action_processors) + (CurrentSumProcessor(('i_a', 'i_e')),) + physical_system_wrappers=tuple(physical_system_wrappers) + (CurrentSumProcessor(('i_a', 'i_e')),) ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_sc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_sc_shunt_dc_env.py index 171efe82..cb0b78d3 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_sc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_sc_shunt_dc_env.py @@ -1,7 +1,7 @@ from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ ElectricMotorVisualization from gym_electric_motor.physical_systems.physical_systems import DcMotorSystem -from gym_electric_motor.state_action_processors import CurrentSumProcessor +from gym_electric_motor.physical_system_wrappers import CurrentSumProcessor from gym_electric_motor.visualization import MotorDashboard from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator from gym_electric_motor import physical_systems as ps @@ -87,7 +87,7 @@ class ContSpeedControlDcShuntMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -148,5 +148,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=tuple(state_action_processors) + (CurrentSumProcessor(('i_a', 'i_e')),) + physical_system_wrappers=tuple(physical_system_wrappers) + (CurrentSumProcessor(('i_a', 'i_e')),) ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_tc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_tc_shunt_dc_env.py index 1166cfd5..ee0da0aa 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_tc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/cont_tc_shunt_dc_env.py @@ -6,7 +6,7 @@ from gym_electric_motor import physical_systems as ps from gym_electric_motor.reward_functions import WeightedSumOfErrors from gym_electric_motor.utils import initialize -from gym_electric_motor.state_action_processors import CurrentSumProcessor +from gym_electric_motor.physical_system_wrappers import CurrentSumProcessor class ContTorqueControlDcShuntMotorEnv(ElectricMotorEnvironment): @@ -87,7 +87,7 @@ class ContTorqueControlDcShuntMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, state_action_processors=()): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-4, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -152,5 +152,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=tuple(state_action_processors) + (CurrentSumProcessor(('i_a', 'i_e')),) + physical_system_wrappers=tuple(physical_system_wrappers) + (CurrentSumProcessor(('i_a', 'i_e')),) ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_cc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_cc_shunt_dc_env.py index 60f97003..00bf0c08 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_cc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_cc_shunt_dc_env.py @@ -1,7 +1,7 @@ from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ ElectricMotorVisualization from gym_electric_motor.physical_systems.physical_systems import DcMotorSystem -from gym_electric_motor.state_action_processors import CurrentSumProcessor +from gym_electric_motor.physical_system_wrappers import CurrentSumProcessor from gym_electric_motor.visualization import MotorDashboard from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator from gym_electric_motor import physical_systems as ps @@ -87,7 +87,7 @@ class FiniteCurrentControlDcShuntMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -146,5 +146,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=tuple(state_action_processors) + (CurrentSumProcessor(('i_a', 'i_e')),) + physical_system_wrappers=tuple(physical_system_wrappers) + (CurrentSumProcessor(('i_a', 'i_e')),) ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_sc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_sc_shunt_dc_env.py index 27bb3c1e..b8b0a13a 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_sc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_sc_shunt_dc_env.py @@ -1,7 +1,7 @@ from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ ElectricMotorVisualization from gym_electric_motor.physical_systems.physical_systems import DcMotorSystem -from gym_electric_motor.state_action_processors import CurrentSumProcessor +from gym_electric_motor.physical_system_wrappers import CurrentSumProcessor from gym_electric_motor.visualization import MotorDashboard from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator from gym_electric_motor import physical_systems as ps @@ -87,7 +87,7 @@ class FiniteSpeedControlDcShuntMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -148,5 +148,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=tuple(state_action_processors) + (CurrentSumProcessor(('i_a', 'i_e')),) + physical_system_wrappers=tuple(physical_system_wrappers) + (CurrentSumProcessor(('i_a', 'i_e')),) ) diff --git a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_tc_shunt_dc_env.py b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_tc_shunt_dc_env.py index 4038c3cb..180fa309 100644 --- a/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_tc_shunt_dc_env.py +++ b/gym_electric_motor/envs/gym_dcm/shunt_dc_motor_env/finite_tc_shunt_dc_env.py @@ -1,7 +1,7 @@ from gym_electric_motor.core import ElectricMotorEnvironment, ReferenceGenerator, RewardFunction, \ ElectricMotorVisualization from gym_electric_motor.physical_systems.physical_systems import DcMotorSystem -from gym_electric_motor.state_action_processors import CurrentSumProcessor +from gym_electric_motor.physical_system_wrappers import CurrentSumProcessor from gym_electric_motor.visualization import MotorDashboard from gym_electric_motor.reference_generators import WienerProcessReferenceGenerator from gym_electric_motor import physical_systems as ps @@ -87,7 +87,7 @@ class FiniteTorqueControlDcShuntMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, state_action_processors=()): + constraints=('i_a', 'i_e'), calc_jacobian=True, tau=1e-5, physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-5. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -144,5 +144,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=tuple(state_action_processors) + (CurrentSumProcessor(('i_a', 'i_e')),) + physical_system_wrappers=tuple(physical_system_wrappers) + (CurrentSumProcessor(('i_a', 'i_e')),) ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_cc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_cc_dfim_env.py index bebb81ce..cc9575b7 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_cc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_cc_dfim_env.py @@ -95,7 +95,7 @@ def __init__( self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=() + physical_system_wrappers=() ): """ Args: @@ -117,7 +117,7 @@ def __init__( tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -174,5 +174,5 @@ def __init__( super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_sc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_sc_dfim_env.py index 9e7042a8..f83b5503 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_sc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_sc_dfim_env.py @@ -94,7 +94,7 @@ class ContSpeedControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -115,7 +115,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -163,5 +163,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_tc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_tc_dfim_env.py index dd233d32..0666944e 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_tc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/cont_tc_dfim_env.py @@ -94,7 +94,7 @@ class ContTorqueControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -115,7 +115,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -167,5 +167,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_cc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_cc_dfim_env.py index 5daeb853..71b83c26 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_cc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_cc_dfim_env.py @@ -94,7 +94,7 @@ class FiniteCurrentControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -115,7 +115,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -172,5 +172,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_sc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_sc_dfim_env.py index c03e0716..130208ad 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_sc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_sc_dfim_env.py @@ -94,7 +94,7 @@ class FiniteSpeedControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -115,7 +115,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -163,5 +163,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_tc_dfim_env.py b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_tc_dfim_env.py index 00e3126c..e230e384 100644 --- a/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_tc_dfim_env.py +++ b/gym_electric_motor/envs/gym_im/doubly_fed_induction_motor_envs/finite_tc_dfim_env.py @@ -94,7 +94,7 @@ class FiniteTorqueControlDoublyFedInductionMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -115,7 +115,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -161,5 +161,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_cc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_cc_scim_env.py index 86a29d31..26b660d0 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_cc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_cc_scim_env.py @@ -92,7 +92,7 @@ class ContCurrentControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -113,7 +113,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -161,5 +161,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_sc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_sc_scim_env.py index 14d3948f..25b592d8 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_sc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_sc_scim_env.py @@ -91,7 +91,7 @@ class ContSpeedControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): """ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), - state_action_processors=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), + physical_system_wrappers=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4): """ Args: @@ -113,7 +113,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -153,5 +153,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_tc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_tc_scim_env.py index c75feafe..b3b7d248 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_tc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/cont_tc_scim_env.py @@ -92,7 +92,7 @@ class ContTorqueControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -113,7 +113,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -156,5 +156,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_cc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_cc_scim_env.py index 574fc5af..f6f495b1 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_cc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_cc_scim_env.py @@ -92,7 +92,7 @@ class FiniteCurrentControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -113,7 +113,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -161,5 +161,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_sc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_sc_scim_env.py index 12b39990..19236fc8 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_sc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_sc_scim_env.py @@ -92,7 +92,7 @@ class FiniteSpeedControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -113,7 +113,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -153,5 +153,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_tc_scim_env.py b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_tc_scim_env.py index 3f0438bd..ad6a677f 100644 --- a/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_tc_scim_env.py +++ b/gym_electric_motor/envs/gym_im/squirrel_cage_induction_motor_envs/finite_tc_scim_env.py @@ -92,7 +92,7 @@ class FiniteTorqueControlSquirrelCageInductionMotorEnv(ElectricMotorEnvironment) def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -113,7 +113,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -150,5 +150,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_pmsm/cont_cc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/cont_cc_pmsm_env.py index 32c08082..058541b2 100644 --- a/gym_electric_motor/envs/gym_pmsm/cont_cc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/cont_cc_pmsm_env.py @@ -87,7 +87,7 @@ class ContCurrentControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironm def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -156,5 +156,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_pmsm/cont_sc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/cont_sc_pmsm_env.py index 2da786bf..2f96ba5c 100644 --- a/gym_electric_motor/envs/gym_pmsm/cont_sc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/cont_sc_pmsm_env.py @@ -87,7 +87,7 @@ class ContSpeedControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironmen def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -146,5 +146,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_pmsm/cont_tc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/cont_tc_pmsm_env.py index b8e98bd2..7722621a 100644 --- a/gym_electric_motor/envs/gym_pmsm/cont_tc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/cont_tc_pmsm_env.py @@ -87,7 +87,7 @@ class ContTorqueControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironme def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -151,5 +151,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_pmsm/finite_cc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/finite_cc_pmsm_env.py index 00c85cee..e8f90edd 100644 --- a/gym_electric_motor/envs/gym_pmsm/finite_cc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/finite_cc_pmsm_env.py @@ -87,7 +87,7 @@ class FiniteCurrentControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviro def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -156,5 +156,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_pmsm/finite_sc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/finite_sc_pmsm_env.py index bd8cb527..4f09c436 100644 --- a/gym_electric_motor/envs/gym_pmsm/finite_sc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/finite_sc_pmsm_env.py @@ -87,7 +87,7 @@ class FiniteSpeedControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironm def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -146,5 +146,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_pmsm/finite_tc_pmsm_env.py b/gym_electric_motor/envs/gym_pmsm/finite_tc_pmsm_env.py index f8b76f19..79fc525b 100644 --- a/gym_electric_motor/envs/gym_pmsm/finite_tc_pmsm_env.py +++ b/gym_electric_motor/envs/gym_pmsm/finite_tc_pmsm_env.py @@ -87,7 +87,7 @@ class FiniteTorqueControlPermanentMagnetSynchronousMotorEnv(ElectricMotorEnviron def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -145,5 +145,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_synrm/cont_cc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/cont_cc_synrm_env.py index 884b3180..69258072 100644 --- a/gym_electric_motor/envs/gym_synrm/cont_cc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/cont_cc_synrm_env.py @@ -87,7 +87,7 @@ class ContCurrentControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -156,5 +156,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_synrm/cont_sc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/cont_sc_synrm_env.py index c27a6087..7d79eff8 100644 --- a/gym_electric_motor/envs/gym_synrm/cont_sc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/cont_sc_synrm_env.py @@ -87,7 +87,7 @@ class ContSpeedControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -147,5 +147,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_synrm/cont_tc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/cont_tc_synrm_env.py index 9f276836..34b319bd 100644 --- a/gym_electric_motor/envs/gym_synrm/cont_tc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/cont_tc_synrm_env.py @@ -87,7 +87,7 @@ class ContTorqueControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-4, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -151,5 +151,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_synrm/finite_cc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/finite_cc_synrm_env.py index a2e8898e..04c8aded 100644 --- a/gym_electric_motor/envs/gym_synrm/finite_cc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/finite_cc_synrm_env.py @@ -87,7 +87,7 @@ class FiniteCurrentControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -156,5 +156,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_synrm/finite_sc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/finite_sc_synrm_env.py index 60695cd4..9c2d3f79 100644 --- a/gym_electric_motor/envs/gym_synrm/finite_sc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/finite_sc_synrm_env.py @@ -87,7 +87,7 @@ class FiniteSpeedControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment): def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -109,7 +109,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -148,5 +148,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/envs/gym_synrm/finite_tc_synrm_env.py b/gym_electric_motor/envs/gym_synrm/finite_tc_synrm_env.py index 0a4fa512..7d86e9e8 100644 --- a/gym_electric_motor/envs/gym_synrm/finite_tc_synrm_env.py +++ b/gym_electric_motor/envs/gym_synrm/finite_tc_synrm_env.py @@ -87,7 +87,7 @@ class FiniteTorqueControlSynchronousReluctanceMotorEnv(ElectricMotorEnvironment) def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solver=None, reward_function=None, reference_generator=None, visualization=None, state_filter=None, callbacks=(), constraints=(SquaredConstraint(('i_sq', 'i_sd')),), calc_jacobian=True, tau=1e-5, - state_action_processors=()): + physical_system_wrappers=()): """ Args: supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment @@ -108,7 +108,7 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve tau(float): Duration of one control step in seconds. Default: 1e-4. state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) callbacks(list(Callback)): Callbacks for user interaction. Default: () - state_action_processors(list(StateActionProcessor)): List of state action processors to modify the + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the actions to and states from the physical system before they are used in the environment. Default: () Note on the env-arg type: @@ -145,5 +145,5 @@ def __init__(self, supply=None, converter=None, motor=None, load=None, ode_solve super().__init__( physical_system=physical_system, reference_generator=reference_generator, reward_function=reward_function, constraints=constraints, visualization=visualization, state_filter=state_filter, callbacks=callbacks, - state_action_processors=state_action_processors + physical_system_wrappers=physical_system_wrappers ) diff --git a/gym_electric_motor/state_action_processors/__init__.py b/gym_electric_motor/physical_system_wrappers/__init__.py similarity index 84% rename from gym_electric_motor/state_action_processors/__init__.py rename to gym_electric_motor/physical_system_wrappers/__init__.py index 95160342..dffce13f 100644 --- a/gym_electric_motor/state_action_processors/__init__.py +++ b/gym_electric_motor/physical_system_wrappers/__init__.py @@ -1,4 +1,4 @@ -from .state_action_processor import StateActionProcessor +from .physical_system_wrapper import PhysicalSystemWrapper from .current_sum_processor import CurrentSumProcessor from .flux_observer import FluxObserver from .dq_to_abc_action_processor import DqToAbcActionProcessor diff --git a/gym_electric_motor/state_action_processors/cos_sin_processor.py b/gym_electric_motor/physical_system_wrappers/cos_sin_processor.py similarity index 96% rename from gym_electric_motor/state_action_processors/cos_sin_processor.py rename to gym_electric_motor/physical_system_wrappers/cos_sin_processor.py index b3ce2fc0..690dee4c 100644 --- a/gym_electric_motor/state_action_processors/cos_sin_processor.py +++ b/gym_electric_motor/physical_system_wrappers/cos_sin_processor.py @@ -1,10 +1,10 @@ import gym import numpy as np -from gym_electric_motor.state_action_processors import StateActionProcessor +from gym_electric_motor.physical_system_wrappers import PhysicalSystemWrapper -class CosSinProcessor(StateActionProcessor): +class CosSinProcessor(PhysicalSystemWrapper): """Adds ``cos(angle)`` and ``sin(angle)`` states to the systems state vector that are the cosine and sine of a certain systems state. diff --git a/gym_electric_motor/state_action_processors/current_sum_processor.py b/gym_electric_motor/physical_system_wrappers/current_sum_processor.py similarity index 95% rename from gym_electric_motor/state_action_processors/current_sum_processor.py rename to gym_electric_motor/physical_system_wrappers/current_sum_processor.py index aaa7cb11..e3f25224 100644 --- a/gym_electric_motor/state_action_processors/current_sum_processor.py +++ b/gym_electric_motor/physical_system_wrappers/current_sum_processor.py @@ -1,10 +1,10 @@ import gym import numpy as np -from gym_electric_motor.state_action_processors import StateActionProcessor +from gym_electric_motor.physical_system_wrappers import PhysicalSystemWrapper -class CurrentSumProcessor(StateActionProcessor): +class CurrentSumProcessor(PhysicalSystemWrapper): """Adds an ``i_sum`` state to the systems state vector that adds up currents.""" def __init__(self, currents, limit='max', physical_system=None): diff --git a/gym_electric_motor/state_action_processors/dead_time_processor.py b/gym_electric_motor/physical_system_wrappers/dead_time_processor.py similarity index 95% rename from gym_electric_motor/state_action_processors/dead_time_processor.py rename to gym_electric_motor/physical_system_wrappers/dead_time_processor.py index 8acf0d6a..39b6a213 100644 --- a/gym_electric_motor/state_action_processors/dead_time_processor.py +++ b/gym_electric_motor/physical_system_wrappers/dead_time_processor.py @@ -2,10 +2,10 @@ import numpy as np import gym.spaces -from gym_electric_motor.state_action_processors import StateActionProcessor +from gym_electric_motor.physical_system_wrappers import PhysicalSystemWrapper -class DeadTimeProcessor(StateActionProcessor): +class DeadTimeProcessor(PhysicalSystemWrapper): """The DeadTimeProcessor delays the actions to the physical system for a parameterizable amount of steps. Reset Actions: @@ -28,7 +28,7 @@ def __init__(self, steps=1, reset_action=None, physical_system=None): steps(int): Number of steps to delay the actions. reset_action(callable): A callable that returns a list of length steps to initialize the dead-actions after a reset. Default: See above in the class description - physical_system(PhysicalSystem (optional)): The inner physical system of this StateActionProcessor. + physical_system(PhysicalSystem (optional)): The inner physical system of this PhysicalSystemWrapper. """ self._reset_actions = reset_action self._steps = int(steps) diff --git a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py b/gym_electric_motor/physical_system_wrappers/dq_to_abc_action_processor.py similarity index 97% rename from gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py rename to gym_electric_motor/physical_system_wrappers/dq_to_abc_action_processor.py index 68a8fbbe..9946f2bf 100644 --- a/gym_electric_motor/state_action_processors/dq_to_abc_action_processor.py +++ b/gym_electric_motor/physical_system_wrappers/dq_to_abc_action_processor.py @@ -2,10 +2,10 @@ import numpy as np import gym_electric_motor.physical_systems as ps -from gym_electric_motor.state_action_processors import StateActionProcessor +from gym_electric_motor.physical_system_wrappers import PhysicalSystemWrapper -class DqToAbcActionProcessor(StateActionProcessor): +class DqToAbcActionProcessor(PhysicalSystemWrapper): """The DqToAbcActionProcessor converts an inner system with an AC motor and actions in abc coordinates to a system to which actions in the dq-coordinate system can be applied. """ diff --git a/gym_electric_motor/state_action_processors/flux_observer.py b/gym_electric_motor/physical_system_wrappers/flux_observer.py similarity index 97% rename from gym_electric_motor/state_action_processors/flux_observer.py rename to gym_electric_motor/physical_system_wrappers/flux_observer.py index 9f446089..bbe49dab 100644 --- a/gym_electric_motor/state_action_processors/flux_observer.py +++ b/gym_electric_motor/physical_system_wrappers/flux_observer.py @@ -2,10 +2,10 @@ import numpy as np import gym_electric_motor as gem -from gym_electric_motor.state_action_processors import StateActionProcessor +from gym_electric_motor.physical_system_wrappers import PhysicalSystemWrapper -class FluxObserver(StateActionProcessor): +class FluxObserver(PhysicalSystemWrapper): """The FluxObserver extends the systems state vector of induction machine environments by estimated flux states ``psi_abs``, and ``psi_angle``. The flux is estimated as follows: diff --git a/gym_electric_motor/state_action_processors/state_action_processor.py b/gym_electric_motor/physical_system_wrappers/physical_system_wrapper.py similarity index 87% rename from gym_electric_motor/state_action_processors/state_action_processor.py rename to gym_electric_motor/physical_system_wrappers/physical_system_wrapper.py index 41719871..92396fee 100644 --- a/gym_electric_motor/state_action_processors/state_action_processor.py +++ b/gym_electric_motor/physical_system_wrappers/physical_system_wrapper.py @@ -1,11 +1,11 @@ import gym_electric_motor as gem -class StateActionProcessor(gem.core.PhysicalSystem, gem.core.RandomComponent): - """A StateActionProcessor is a wrapper around the PhysicalSystem of a gem-environment. +class PhysicalSystemWrapper(gem.core.PhysicalSystem, gem.core.RandomComponent): + """A PhysicalSystemWrapper is a wrapper around the PhysicalSystem of a gem-environment. It may be used to modify its states and actions. In contrast to gym-wrappers which are put around the whole - environment, modified states by the StateActionProcessors can be referenced, rewarded and visualized by the + environment, modified states by the PhysicalSystemWrappers can be referenced, rewarded and visualized by the other components of the environment. """ @@ -41,7 +41,7 @@ def state_space(self, space): @property def physical_system(self): - """The next inner physical_system or the next inner state_action_processor.""" + """The next inner physical_system or the next inner physical_system_wrapper.""" return self._physical_system @property @@ -55,7 +55,7 @@ def limits(self): @property def unwrapped(self): - """The innermost physical system within all state-action processors.""" + """The innermost physical system within all Physical System Wrappers.""" return self.physical_system.unwrapped @property @@ -91,10 +91,10 @@ def __init__(self, physical_system=None): self.set_physical_system(physical_system) def set_physical_system(self, physical_system): - """Sets the inner physical system of this StateActionProcessor. + """Sets the inner physical system of this PhysicalSystemWrapper. Args: - physical_system(PhysicalSystem): The inner physical system or state action processor. + physical_system(PhysicalSystem): The inner physical system or Physical System Wrapper. """ self._physical_system = physical_system self._action_space = physical_system.action_space diff --git a/gym_electric_motor/state_action_processors/state_noise_processor.py b/gym_electric_motor/physical_system_wrappers/state_noise_processor.py similarity index 92% rename from gym_electric_motor/state_action_processors/state_noise_processor.py rename to gym_electric_motor/physical_system_wrappers/state_noise_processor.py index 5e73d304..4cff64ac 100644 --- a/gym_electric_motor/state_action_processors/state_noise_processor.py +++ b/gym_electric_motor/physical_system_wrappers/state_noise_processor.py @@ -1,7 +1,7 @@ -from gym_electric_motor.state_action_processors import StateActionProcessor +from gym_electric_motor.physical_system_wrappers import PhysicalSystemWrapper -class StateNoiseProcessor(StateActionProcessor): +class StateNoiseProcessor(PhysicalSystemWrapper): """The StateNoiseProcessor puts additional noise onto the systems state. The random distribution of the noise can be selected by those available in the numpy random generator: @@ -10,12 +10,12 @@ class StateNoiseProcessor(StateActionProcessor): Example: .. code-block:: python import gym_electric_motor as gem - state_noise_processor = gem.state_action_processors.StateNoiseProcessor( + state_noise_processor = gem.physical_system_wrappers.StateNoiseProcessor( states=['omega', 'torque'], random_dist='laplace' random_kwargs=dict(loc=0.0, scale=0.1) ) - env = gem.make('Cont-SC-PermExDc-v0', state_action_processors=(state_noise_processor,)) + env = gem.make('Cont-SC-PermExDc-v0', physical_system_wrappers=(state_noise_processor,)) """ diff --git a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py index 433b83c8..63beab08 100644 --- a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py @@ -169,8 +169,12 @@ def _torque_limit(self): i_n = self.nominal_values['i'] _p = mp['l_m'] * i_n / (2 * (mp['l_d'] - mp['l_q'])) _q = - i_n ** 2 / 2 - i_d_opt = - _p / 2 - np.sqrt( (_p / 2) ** 2 - _q) - i_q_opt = np.sqrt(i_n ** 2 - i_d_opt ** 2) + i_d_opt = - _p / 2 - np.sqrt((_p / 2) ** 2 - _q) + i_q_opt_2 = i_n ** 2 - i_d_opt ** 2 + if i_q_opt_2 >= 0: + i_q_opt = np.sqrt(i_q_opt_2) + else: + i_q_opt = -np.sqrt(-i_q_opt_2) return self.torque([i_d_opt, i_q_opt, self._limits['i_e'], 0]) def torque(self, currents): diff --git a/tests/test_state_action_processors/__init__.py b/tests/test_physical_system_wrappers/__init__.py similarity index 100% rename from tests/test_state_action_processors/__init__.py rename to tests/test_physical_system_wrappers/__init__.py diff --git a/tests/test_state_action_processors/test_cos_sin_processor.py b/tests/test_physical_system_wrappers/test_cos_sin_processor.py similarity index 85% rename from tests/test_state_action_processors/test_cos_sin_processor.py rename to tests/test_physical_system_wrappers/test_cos_sin_processor.py index e1c80286..279bbabc 100644 --- a/tests/test_state_action_processors/test_cos_sin_processor.py +++ b/tests/test_physical_system_wrappers/test_cos_sin_processor.py @@ -4,14 +4,14 @@ import gym_electric_motor as gem from tests.testing_utils import DummyPhysicalSystem -from .test_state_action_processor import TestStateActionProcessor +from .test_physical_system_wrapper import TestPhysicalSystemWrapper -class TestCosSinProcessor(TestStateActionProcessor): +class TestCosSinProcessor(TestPhysicalSystemWrapper): @pytest.fixture def processor(self, physical_system): - return gem.state_action_processors.CosSinProcessor(angle='dummy_state_0', physical_system=physical_system) + return gem.physical_system_wrappers.CosSinProcessor(angle='dummy_state_0', physical_system=physical_system) def test_limits(self, processor, physical_system): assert all(processor.limits == np.concatenate((physical_system.limits, [1., 1.]))) diff --git a/tests/test_state_action_processors/test_dead_time_processor.py b/tests/test_physical_system_wrappers/test_dead_time_processor.py similarity index 78% rename from tests/test_state_action_processors/test_dead_time_processor.py rename to tests/test_physical_system_wrappers/test_dead_time_processor.py index 0b22049a..d325ee78 100644 --- a/tests/test_state_action_processors/test_dead_time_processor.py +++ b/tests/test_physical_system_wrappers/test_dead_time_processor.py @@ -3,19 +3,19 @@ import numpy as np import gym_electric_motor as gem -from .test_state_action_processor import TestStateActionProcessor +from .test_physical_system_wrapper import TestPhysicalSystemWrapper from tests.testing_utils import DummyPhysicalSystem -class TestDeadTimeProcessor(TestStateActionProcessor): +class TestDeadTimeProcessor(TestPhysicalSystemWrapper): @pytest.fixture def processor(self, physical_system): - return gem.state_action_processors.DeadTimeProcessor(physical_system=physical_system) + return gem.physical_system_wrappers.DeadTimeProcessor(physical_system=physical_system) @pytest.fixture def unset_processor(self, physical_system): - return gem.state_action_processors.DeadTimeProcessor() + return gem.physical_system_wrappers.DeadTimeProcessor() @pytest.mark.parametrize('action', [np.array([5.0]), np.array([2.0])]) def test_simulate(self, reset_processor, physical_system, action): @@ -24,9 +24,9 @@ def test_simulate(self, reset_processor, physical_system, action): assert all(physical_system.action == np.array([0.0])) @pytest.mark.parametrize('unset_processor', [ - gem.state_action_processors.DeadTimeProcessor(steps=2), - gem.state_action_processors.DeadTimeProcessor(steps=1), - gem.state_action_processors.DeadTimeProcessor(steps=5), + gem.physical_system_wrappers.DeadTimeProcessor(steps=2), + gem.physical_system_wrappers.DeadTimeProcessor(steps=1), + gem.physical_system_wrappers.DeadTimeProcessor(steps=5), ]) @pytest.mark.parametrize( ['action_space', 'actions', 'reset_action'], @@ -65,7 +65,7 @@ def test_execution(self, unset_processor, physical_system, action_space, actions except ValueError: assert all(physical_system.action == expected_actions[i]) - @pytest.mark.parametrize('processor', [gem.state_action_processors.DeadTimeProcessor()]) + @pytest.mark.parametrize('processor', [gem.physical_system_wrappers.DeadTimeProcessor()]) def test_false_action_space(self, processor, physical_system): physical_system._action_space = gym.spaces.MultiBinary(5) with pytest.raises(AssertionError): @@ -74,4 +74,4 @@ def test_false_action_space(self, processor, physical_system): @pytest.mark.parametrize('steps', [0, -10]) def test_false_steps(self, steps): with pytest.raises(AssertionError): - assert gem.state_action_processors.DeadTimeProcessor(steps) + assert gem.physical_system_wrappers.DeadTimeProcessor(steps) diff --git a/tests/test_state_action_processors/test_dq_to_abc_action_processor.py b/tests/test_physical_system_wrappers/test_dq_to_abc_action_processor.py similarity index 83% rename from tests/test_state_action_processors/test_dq_to_abc_action_processor.py rename to tests/test_physical_system_wrappers/test_dq_to_abc_action_processor.py index 5fe26fc6..84de3efc 100644 --- a/tests/test_state_action_processors/test_dq_to_abc_action_processor.py +++ b/tests/test_physical_system_wrappers/test_dq_to_abc_action_processor.py @@ -4,10 +4,10 @@ import gym_electric_motor as gem from ..testing_utils import DummyPhysicalSystem -from .test_state_action_processor import TestStateActionProcessor +from .test_physical_system_wrapper import TestPhysicalSystemWrapper -class TestDqToAbcActionProcessor(TestStateActionProcessor): +class TestDqToAbcActionProcessor(TestPhysicalSystemWrapper): @pytest.fixture def physical_system(self): @@ -17,7 +17,7 @@ def physical_system(self): @pytest.fixture def processor(self, physical_system): - return gem.state_action_processors.DqToAbcActionProcessor.make('PMSM', physical_system=physical_system) + return gem.physical_system_wrappers.DqToAbcActionProcessor.make('PMSM', physical_system=physical_system) def test_action_space(self, processor, physical_system): space = gym.spaces.Box(-1, 1, shape=(2,), dtype=np.float64) diff --git a/tests/test_state_action_processors/test_flux_observer.py b/tests/test_physical_system_wrappers/test_flux_observer.py similarity index 88% rename from tests/test_state_action_processors/test_flux_observer.py rename to tests/test_physical_system_wrappers/test_flux_observer.py index 4ac1839e..1061bf95 100644 --- a/tests/test_state_action_processors/test_flux_observer.py +++ b/tests/test_physical_system_wrappers/test_flux_observer.py @@ -4,11 +4,11 @@ import gym_electric_motor as gem from tests.testing_utils import DummyPhysicalSystem -from tests.utils import StateActionTestProcessor -from .test_state_action_processor import TestStateActionProcessor +from tests.utils import PhysicalSystemTestWrapper +from .test_physical_system_wrapper import TestPhysicalSystemWrapper -class TestFluxObserver(TestStateActionProcessor): +class TestFluxObserver(TestPhysicalSystemWrapper): @pytest.fixture def physical_system(self): @@ -27,7 +27,7 @@ def reset_physical_system(self, physical_system): @pytest.fixture def processor(self, physical_system): - return gem.state_action_processors.FluxObserver(physical_system=physical_system) + return gem.physical_system_wrappers.FluxObserver(physical_system=physical_system) @pytest.fixture def reset_processor(self, processor): diff --git a/tests/test_state_action_processors/test_state_action_processor.py b/tests/test_physical_system_wrappers/test_physical_system_wrapper.py similarity index 87% rename from tests/test_state_action_processors/test_state_action_processor.py rename to tests/test_physical_system_wrappers/test_physical_system_wrapper.py index 688b4ae1..b4ddef7d 100644 --- a/tests/test_state_action_processors/test_state_action_processor.py +++ b/tests/test_physical_system_wrappers/test_physical_system_wrapper.py @@ -4,7 +4,7 @@ import tests.testing_utils as tu -class TestStateActionProcessor: +class TestPhysicalSystemWrapper: @pytest.fixture def physical_system(self): @@ -12,7 +12,7 @@ def physical_system(self): @pytest.fixture def processor(self, physical_system): - return gem.state_action_processors.StateActionProcessor(physical_system=physical_system) + return gem.physical_system_wrappers.PhysicalSystemWrapper(physical_system=physical_system) @pytest.fixture def reset_processor(self, processor): @@ -21,7 +21,7 @@ def reset_processor(self, processor): @pytest.fixture def double_wrapped(self, processor): - return gem.state_action_processors.StateActionProcessor(physical_system=processor) + return gem.physical_system_wrappers.PhysicalSystemWrapper(physical_system=processor) def test_action_space(self, processor, physical_system): assert processor.action_space == physical_system.action_space diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py index 511e55e5..cd6c06cb 100644 --- a/tests/utils/__init__.py +++ b/tests/utils/__init__.py @@ -1 +1 @@ -from .state_action_test_processor import StateActionTestProcessor +from .physical_system_test_wrapper import PhysicalSystemTestWrapper diff --git a/tests/utils/state_action_test_processor.py b/tests/utils/physical_system_test_wrapper.py similarity index 82% rename from tests/utils/state_action_test_processor.py rename to tests/utils/physical_system_test_wrapper.py index 45c4ed44..5640abd2 100644 --- a/tests/utils/state_action_test_processor.py +++ b/tests/utils/physical_system_test_wrapper.py @@ -1,7 +1,7 @@ import gym_electric_motor as gem -class StateActionTestProcessor(gem.state_action_processors.StateActionProcessor): +class PhysicalSystemTestWrapper(gem.physical_system_wrappers.PhysicalSystemWrapper): def __init__(self, **kwargs): super().__init__(**kwargs) From e9c938a3ac8eb2e92919805b23a86ea3d07fdf2b Mon Sep 17 00:00:00 2001 From: Arne Date: Tue, 24 May 2022 21:34:06 +0200 Subject: [PATCH 77/79] eesm torque limit equation fix --- .../externally_excited_synchronous_motor.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py index 63beab08..68cf6efd 100644 --- a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py @@ -169,13 +169,12 @@ def _torque_limit(self): i_n = self.nominal_values['i'] _p = mp['l_m'] * i_n / (2 * (mp['l_d'] - mp['l_q'])) _q = - i_n ** 2 / 2 - i_d_opt = - _p / 2 - np.sqrt((_p / 2) ** 2 - _q) - i_q_opt_2 = i_n ** 2 - i_d_opt ** 2 - if i_q_opt_2 >= 0: - i_q_opt = np.sqrt(i_q_opt_2) + if mp['l_d'] < mp['l_q']: + i_d_opt = - _p / 2 - np.sqrt( (_p / 2) ** 2 - _q) else: - i_q_opt = -np.sqrt(-i_q_opt_2) - return self.torque([i_d_opt, i_q_opt, self._limits['i_e'], 0]) + i_d_opt = - _p / 2 + np.sqrt( (_p / 2) ** 2 - _q) + i_q_opt = np.sqrt(i_n ** 2 - i_d_opt ** 2) + return self.torque([i_d_opt, i_q_opt, self._limits['i_e'], 0]) def torque(self, currents): # Docstring of superclass From e02cceb019738ed6b55ecaed585292bc8a518a73 Mon Sep 17 00:00:00 2001 From: Arne Date: Tue, 24 May 2022 21:52:08 +0200 Subject: [PATCH 78/79] EESM torque limit fix --- .../electric_motors/externally_excited_synchronous_motor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py index 68cf6efd..5a32a352 100644 --- a/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py +++ b/gym_electric_motor/physical_systems/electric_motors/externally_excited_synchronous_motor.py @@ -162,7 +162,6 @@ def electrical_ode(self, state, u_dq, omega, *_): def _torque_limit(self): # Docstring of superclass mp = self._motor_parameter - # I am not sure about this if mp['l_d'] == mp['l_q']: return self.torque([0, self._limits['i_sq'], self._limits['i_e'], 0]) else: From d2a08b2247315be84df95f2a0d516892a4b44a5c Mon Sep 17 00:00:00 2001 From: Arne Date: Wed, 8 Jun 2022 22:14:26 +0200 Subject: [PATCH 79/79] Restricted gym version. gym>=0.24.0 has a bug concerning the Tuple-observation spaces in combination with the new env-checking --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1d772b2a..36077ac8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ matplotlib>=3.1.2 numpy>=1.16.4 scipy>=1.4.1 -gym>=0.15.4 +gym<0.24.0,>=0.15.4 pytest>=5.2.2 pytest-cov