Source code for gym_electric_motor.physical_systems.converters

import numpy as np
from gymnasium.spaces import Box, Discrete, MultiDiscrete


[docs]class PowerElectronicConverter: """ Base class for all converters in a SCMLSystem. Properties: | *voltages(tuple(float, float))*: Determines which output voltage polarities the converter can generate. | E.g. (0, 1) - Only positive voltages / (-1, 1) Positive and negative voltages | *currents(tuple(float, float))*: Determines which output current polarities the converter can generate. | E.g. (0, 1) - Only positive currents / (-1, 1) Positive and negative currents """ #: gymnasium.Space that defines Minimum, Maximum and Dimension of possible output voltage of the converter voltages = None #: gymnasium.Space that defines Minimum, Maximum and Dimension of possible output current of the converter currents = None #: gymnasium.Space that defines the set of all possible actions for the converter action_space = None #: 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, interlocking_time=0.0): """ :param tau: Discrete time step of the system in seconds :param interlocking_time: Interlocking time of the transistors in seconds """ self._tau = tau self._interlocking_time = interlocking_time self._action_start_time = 0.0 self._current_action = self._reset_action
[docs] def reset(self): """ Reset all converter states to a default. Returns: list(float): A default output voltage after reset(=0V). """ self._current_action = self._reset_action self._action_start_time = 0.0 return [0.0]
[docs] def set_action(self, action, t): """ Set the next action of the converter at the beginning of a simulation step in the system. Args: action(element of action_space): The control action on the converter. t(float): Time at the beginning of the simulation step in seconds. Returns: list(float): Times when a switching action occurs and the conversion function must be called by the system. """ self._action_start_time = t self._current_action = action return self._set_switching_pattern(action)
[docs] def i_sup(self, i_out): """ Calculate the current, the converter takes from the supply for the given output currents and the current switching state. Args: i_out(list(float)): All currents flowing out of the converter and into the motor. Returns: float: The current drawn from the supply. """ raise NotImplementedError
[docs] def convert(self, i_out, t): """ The conversion function that converts the previously set action to an input voltage for the motor. This function has to be called at least at every previously defined switching time, because the input voltage for the motor might change at these times. Args: i_out(list(float)): All currents that flow out of the converter into the motor. t(float): Current time of the system. Returns: list(float): List of all input voltages at the motor. """ raise NotImplementedError
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. """ self._switching_pattern = [action] return [self._action_start_time + self._tau]
[docs]class NoConverter(PowerElectronicConverter): """Dummy Converter class used to directly transfer the supply voltage to the motor""" # Dummy default values for voltages and currents. # No real use other than to fit the current physical system architecture voltages = Box(0, 1, shape=(3,), dtype=np.float64) currents = Box(0, 1, shape=(3,), dtype=np.float64) action_space = Box(low=np.array([]), high=np.array([]), dtype=np.float64)
[docs] def i_sup(self, i_out): return i_out[0]
[docs] def convert(self, i_out, t): return [1]
[docs]class ContDynamicallyAveragedConverter(PowerElectronicConverter): """ Base class for all continuously controlled converters that calculate the input voltages to the motor with a dynamically averaged model over one time step. This class also implements the interlocking time of the transistors as a discount on the output voltage. """ _reset_action = [0] def __init__(self, tau=1e-4, **kwargs): # Docstring in base class super().__init__(tau=tau, **kwargs)
[docs] def set_action(self, action, t): # Docstring in base class return super().set_action(min(max(action, self.action_space.low), self.action_space.high), t)
[docs] def convert(self, i_out, t): # Docstring in base class return [ min( max( self._convert(i_out, t) - self._interlock(i_out, t), self.voltages.low[0], ), self.voltages.high[0], ) ]
def _convert(self, i_in, t): """ Calculate an idealized output voltage for the current active action neglecting interlocking times. Args: i_in(list(float)): Input currents of the motor t(float): Time of the system Returns: float: Idealized output voltage neglecting interlocking times """ raise NotImplementedError
[docs] def i_sup(self, i_out): # Docstring in base class raise NotImplementedError
def _interlock(self, i_in, *_): """ Calculate the output voltage discount due to the interlocking time of the transistors Args: i_in(list(float)): list of all currents flowing into the motor. """ return np.sign(i_in[0]) / self._tau * self._interlocking_time
[docs]class FiniteConverter(PowerElectronicConverter): """ Base class for all finite converters. """ #: The switching states of the converter for the current action _switching_pattern = [] #: The current switching state of the converter _switching_state = 0 #: The action that is the default after reset _reset_action = 0 def __init__(self, tau=1e-5, **kwargs): # Docstring in base class super().__init__(tau=tau, **kwargs)
[docs] def set_action(self, action, t): assert self.action_space.contains( action ), f"The selected action {action} is not a valid element of the action space {self.action_space}." return super().set_action(action, t)
[docs] def convert(self, i_out, t): # Docstring in base class raise NotImplementedError
[docs] def i_sup(self, i_out): # Docstring in base class raise NotImplementedError
[docs]class FiniteOneQuadrantConverter(FiniteConverter): """ Key: 'Finite-1QC' Switching States / Actions: | 0: Transistor off. | 1: Transistor on. Action Space: Discrete(2) Output Voltages and Currents: | voltages: Box(0, 1, shape=(1,)) | currents: Box(0, 1, shape=(1,)) """ voltages = Box(0, 1, shape=(1,), dtype=np.float64) currents = Box(0, 1, shape=(1,), dtype=np.float64) action_space = Discrete(2)
[docs] def convert(self, i_out, t): # Docstring in base class return [self._current_action if i_out[0] >= 0 else 1]
[docs] def i_sup(self, i_out): # Docstring in base class return i_out[0] if self._current_action == 1 else 0
[docs]class FiniteTwoQuadrantConverter(FiniteConverter): """ Key: 'Finite-2QC' Switching States / Actions: | 0: Both Transistors off. | 1: Upper Transistor on. | 2: Lower Transistor on. Action Space: Discrete(3) Output Voltages and Currents: | voltages: Box(0, 1, shape=(1,)) | currents: Box(-1, 1, shape=(1,)) """ voltages = Box(0, 1, shape=(1,), dtype=np.float64) currents = Box(-1, 1, shape=(1,), dtype=np.float64) action_space = Discrete(3)
[docs] def convert(self, i_out, t): # Docstring in base class # Converter switches slightly (tau / 1000 seconds) before interlocking time due to inaccuracy of the solvers. if t - self._tau / 1000 > self._action_start_time + self._interlocking_time: self._switching_state = self._switching_pattern[-1] else: self._switching_state = self._switching_pattern[0] if self._switching_state == 0: if i_out[0] < 0: return [1] elif i_out[0] >= 0: return [0.0] elif self._switching_state == 1: return [1] elif self._switching_state == 2: return [0.0] else: raise Exception("Invalid switching state of the converter")
[docs] def i_sup(self, i_out): # Docstring in base class if self._switching_state == 0: return i_out[0] if i_out[0] < 0 else 0 elif self._switching_state == 1: return i_out[0] elif self._switching_state == 2: return 0 else: raise Exception("Invalid switching state of the converter")
def _set_switching_pattern(self, action): # Docstring in base class if action == 0 or self._switching_state == 0 or action == self._switching_state or self._interlocking_time == 0: self._switching_pattern = [action] return [self._action_start_time + self._tau] else: self._switching_pattern = [0, action] return [ self._action_start_time + self._interlocking_time, self._action_start_time + self._tau, ]
[docs]class FiniteFourQuadrantConverter(FiniteConverter): """ Key: 'Finite-4QC' Switching States / Actions: | 0: T2, T4 on. | 1: T1, T4 on. | 2: T2, T3 on. | 3: T1, T3 on. Action Space: Discrete(4) Output Voltages and Currents: | Box(-1, 1, shape=(1,)) | Box(-1, 1, shape=(1,)) """ voltages = Box(-1, 1, shape=(1,), dtype=np.float64) currents = Box(-1, 1, shape=(1,), dtype=np.float64) action_space = Discrete(4) def __init__(self, **kwargs): # Docstring in base class super().__init__(**kwargs) self._subconverters = [ FiniteTwoQuadrantConverter(**kwargs), FiniteTwoQuadrantConverter(**kwargs), ]
[docs] def reset(self): # Docstring in base class self._subconverters[0].reset() self._subconverters[1].reset() return super().reset()
[docs] def convert(self, i_out, t): # Docstring in base class return [self._subconverters[0].convert(i_out, t)[0] - self._subconverters[1].convert([-i_out[0]], t)[0]]
[docs] def set_action(self, action, t): # Docstring in base class assert self.action_space.contains( action ), f"The selected action {action} is not a valid element of the action space {self.action_space}." times = [] action0 = [1, 1, 2, 2][action] action1 = [1, 2, 1, 2][action] times += self._subconverters[0].set_action(action0, t) times += self._subconverters[1].set_action(action1, t) return sorted(list(set(times)))
[docs] def i_sup(self, i_out): # Docstring in base class return self._subconverters[0].i_sup(i_out) + self._subconverters[1].i_sup([-i_out[0]])
[docs]class ContOneQuadrantConverter(ContDynamicallyAveragedConverter): """ Key: 'Cont1QC' Action: Duty Cycle of the Transistor in [0,1]. Action Space: Box([0,1]) Output Voltages and Currents: | voltages: Box(0, 1, shape=(1,)) | currents: Box(0, 1, shape=(1,)) """ voltages = Box(0, 1, shape=(1,), dtype=np.float64) currents = Box(0, 1, shape=(1,), dtype=np.float64) action_space = Box(0, 1, shape=(1,), dtype=np.float64) def _convert(self, i_in, *_): # Docstring in base class return self._current_action[0] if i_in[0] >= 0 else 1 def _interlock(self, *_): # Docstring in base class return 0
[docs] def i_sup(self, i_out): # Docstring in base class return self._current_action[0] * i_out[0]
[docs]class ContTwoQuadrantConverter(ContDynamicallyAveragedConverter): """ Key: 'Cont-2QC' Actions: | Duty Cycle upper Transistor: Action | Duty Cycle upper Transistor: 1 - Action Action Space: Box([0,1]) Output Voltages and Currents: | voltages: Box(0, 1, shape=(1,)) | currents: Box(-1, 1, shape=(1,)) """ voltages = Box(0, 1, shape=(1,), dtype=np.float64) 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]
[docs] def i_sup(self, i_out): # Docstring in base class interlocking_current = 1 if i_out[0] < 0 else 0 return ( self._current_action[0] + self._interlocking_time / self._tau * (interlocking_current - self._current_action[0]) ) * i_out[0]
[docs]class ContFourQuadrantConverter(ContDynamicallyAveragedConverter): """ The continuous four quadrant converter (4QC) is simulated with two continuous 2QC. Key: 'Cont-4QC' Actions: | Duty Cycle Transistor T1: 0.5 * (Action + 1) | Duty Cycle Transistor T2: 1 - 0.5 * (Action + 1) | Duty Cycle Transistor T3: 1 - 0.5 * (Action + 1) | Duty Cycle Transistor T4: 0.5 * (Action + 1) Action Space: Box(-1, 1, shape=(1,)) Output Voltages and Currents: | voltages: Box(-1, 1, shape=(1,)) | currents: Box(-1, 1, shape=(1,)) """ voltages = Box(-1, 1, shape=(1,), dtype=np.float64) currents = Box(-1, 1, shape=(1,), dtype=np.float64) action_space = Box(-1, 1, shape=(1,), dtype=np.float64) def __init__(self, **kwargs): # Docstring in base class super().__init__(**kwargs) self._subconverters = [ ContTwoQuadrantConverter(**kwargs), ContTwoQuadrantConverter(**kwargs), ] def _convert(self, *_): # Not used here pass
[docs] def reset(self): # Docstring in base class self._subconverters[0].reset() self._subconverters[1].reset() return super().reset()
[docs] def convert(self, i_out, t): # Docstring in base class return [self._subconverters[0].convert(i_out, t)[0] - self._subconverters[1].convert(i_out, t)[0]]
[docs] def set_action(self, action, t): # Docstring in base class super().set_action(action, t) times = [] times += self._subconverters[0].set_action([0.5 * (action[0] + 1)], t) times += self._subconverters[1].set_action([-0.5 * (action[0] - 1)], t) return sorted(list(set(times)))
[docs] def i_sup(self, i_out): # Docstring in base class return self._subconverters[0].i_sup(i_out) + self._subconverters[1].i_sup([-i_out[0]])
[docs]class FiniteMultiConverter(FiniteConverter): """ Converter that allows to include an arbitrary number of independent finite subconverters. Subconverters must be 'elementary' and can not be MultiConverters. Key: 'Finite-Multi' Actions: Concatenation of the subconverters' action spaces Action Space: MultiDiscrete([subconverter[0].action_space.n , subconverter[1].action_space.n, ...]) Output Voltage Space: Box([subconverter[0].voltages.low, subconverter[1].voltages.low, ...], [subconverter[0].voltages.high, subconverter[1].voltages.high, ...]) """ @property def tau(self): return self._tau @tau.setter def tau(self, value): self._tau = float(value) for sub_converter in self._sub_converters: sub_converter.tau = value def sub_converters(self): return self._sub_converters def __init__(self, subconverters, **kwargs): """ Args: subconverters(list(str/class/object): Subconverters to instantiate . kwargs(dict): Parameters to pass to the Subconverters and the superclass """ super().__init__(**kwargs) self._sub_converters = [] for subconverter in subconverters: assert not isinstance(subconverter, str) if isinstance(subconverter, type): subconverter = subconverter(**kwargs) self._sub_converters.append(subconverter) self.subsignal_current_space_dims = [] self.subsignal_voltage_space_dims = [] self.action_space = [] currents_low = [] currents_high = [] voltages_low = [] voltages_high = [] # get the limits and space dims from each subconverter 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) self.action_space.append(subconverter.action_space.n) currents_low.append(subconverter.currents.low) currents_high.append(subconverter.currents.high) voltages_low.append(subconverter.voltages.low) voltages_high.append(subconverter.voltages.high) # convert to 1D list self.subsignal_current_space_dims = np.array(self.subsignal_current_space_dims) self.subsignal_voltage_space_dims = np.array(self.subsignal_voltage_space_dims) currents_low = np.concatenate(currents_low) currents_high = np.concatenate(currents_high) voltages_low = np.concatenate(voltages_low) voltages_high = np.concatenate(voltages_high) # put limits into gym_space format self.action_space = MultiDiscrete(self.action_space) self.currents = Box(currents_low, currents_high, dtype=np.float64) self.voltages = Box(voltages_low, voltages_high, dtype=np.float64)
[docs] def convert(self, i_out, t): # Docstring in base class u_in = [] subsignal_idx_low = 0 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 return u_in
[docs] def reset(self): # Docstring in base class u_in = [] for subconverter in self._sub_converters: u_in += subconverter.reset() return u_in
[docs] def set_action(self, action, t): # Docstring in base class times = [] for subconverter, sub_action in zip(self._sub_converters, action): times += subconverter.set_action(sub_action, t) return sorted(list(set(times)))
[docs] 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._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 return i_sup
[docs]class ContMultiConverter(ContDynamicallyAveragedConverter): """ 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' Actions: Concatenation of the subconverters' action spaces Action Space: Box([subconverter[0].action_space.low, subconverter[1].action_space.low, ...], [subconverter[0].action_space.high, subconverter[1].action_space.high, ...]) Output Voltage Space: Box([subconverter[0].voltages.low, subconverter[1].voltages.low, ...], [subconverter[0].voltages.high, subconverter[1].voltages.high, ...]) """ @property def tau(self): return self._tau @tau.setter def tau(self, value): self._tau = float(value) for sub_converter in self._sub_converters: sub_converter.tau = value def __init__(self, subconverters, **kwargs): """ Args: subconverters(list(str/class/object): Subconverters to instantiate . kwargs(dict): Parameters to pass to the Subconverters """ super().__init__(**kwargs) self._sub_converters = [] for subconverter in subconverters: assert not isinstance(subconverter, str) if isinstance(subconverter, type): subconverter = subconverter(**kwargs) self._sub_converters.append(subconverter) self.subsignal_current_space_dims = [] self.subsignal_voltage_space_dims = [] action_space_low = [] action_space_high = [] currents_low = [] currents_high = [] voltages_low = [] voltages_high = [] # get the limits and space dims from each subconverter 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) action_space_low.append(subconverter.action_space.low) action_space_high.append(subconverter.action_space.high) currents_low.append(subconverter.currents.low) currents_high.append(subconverter.currents.high) voltages_low.append(subconverter.voltages.low) voltages_high.append(subconverter.voltages.high) # convert to 1D list self.subsignal_current_space_dims = np.array(self.subsignal_current_space_dims) self.subsignal_voltage_space_dims = np.array(self.subsignal_voltage_space_dims) action_space_low = np.concatenate(action_space_low) action_space_high = np.concatenate(action_space_high) currents_low = np.concatenate(currents_low) currents_high = np.concatenate(currents_high) voltages_low = np.concatenate(voltages_low) voltages_high = np.concatenate(voltages_high) # put limits into gym_space format self.action_space = Box(action_space_low, action_space_high, dtype=np.float64) self.currents = Box(currents_low, currents_high, dtype=np.float64) self.voltages = Box(voltages_low, voltages_high, dtype=np.float64)
[docs] def set_action(self, action, t): # Docstring in base class times = [] ind = 0 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) return sorted(list(set(times)))
[docs] def reset(self): # Docstring in base class u_in = [] for subconverter in self._sub_converters: u_in += subconverter.reset() return u_in
[docs] def convert(self, i_out, t): # Docstring in base class u_in = [] subsignal_idx_low = 0 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 return u_in
def _convert(self, i_in, t): # Not used pass
[docs] 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._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 return i_sup
[docs]class FiniteB6BridgeConverter(FiniteConverter): """ The finite B6 bridge converters (B6C) is simulated with three finite 2QC. Key: 'Finite-B6C' Actions: +-+-----+-----+-----+ | |H_1 |H_2 |H_3 | +=+=====+=====+=====+ |0|lower|lower|lower| +-+-----+-----+-----+ |1|lower|lower|upper| +-+-----+-----+-----+ |2|lower|upper|lower| +-+-----+-----+-----+ |3|lower|upper|upper| +-+-----+-----+-----+ |4|upper|lower|lower| +-+-----+-----+-----+ |5|upper|lower|upper| +-+-----+-----+-----+ |6|upper|upper|lower| +-+-----+-----+-----+ |7|upper|upper|upper| +-+-----+-----+-----+ Action Space: Discrete(8) Output Voltages and Currents: | voltages: Box(-1,1, shape=(3,)) | currents: Box(-1,1, shape=(3,)) Output Voltage Space: Box(-0.5, 0.5, shape=(3,)) """ action_space = Discrete(8) # Only positive voltages can be applied voltages = Box(-1, 1, shape=(3,), dtype=np.float64) # positive and negative currents are possible currents = Box(-1, 1, shape=(3,), dtype=np.float64) _reset_action = 0 _subactions = [ [2, 2, 2], [2, 2, 1], [2, 1, 2], [2, 1, 1], [1, 2, 2], [1, 2, 1], [1, 1, 2], [1, 1, 1], ] 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), ]
[docs] def reset(self): # Docstring in base class return [ self._sub_converters[0].reset()[0] - 0.5, self._sub_converters[1].reset()[0] - 0.5, self._sub_converters[2].reset()[0] - 0.5, ]
[docs] def convert(self, i_out, t): # Docstring in base class u_out = [ 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
[docs] def set_action(self, action, t): # Docstring in base class assert self.action_space.contains( action ), f"The selected action {action} is not a valid element of the action space {self.action_space}." subactions = self._subactions[action] times = [] 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)))
[docs] def i_sup(self, i_out): # Docstring in base class return sum([subconverter.i_sup([i_out_]) for subconverter, i_out_ in zip(self._sub_converters, i_out)])
[docs]class ContB6BridgeConverter(ContDynamicallyAveragedConverter): """ The continuous B6 bridge converter (B6C) is simulated with three continuous 2QC. Key: 'Cont-B6C' Actions: The Duty Cycle for each half bridge in the range of (-1,1) Action Space: Box(-1, 1, shape=(3,)) Output Voltages and Currents: | voltages: Box(-1,1, shape=(3,)) | currents: Box(-1,1, shape=(3,)) Output Voltage Space: Box(-0.5, 0.5, shape=(3,)) """ action_space = Box(-1, 1, shape=(3,), dtype=np.float64) # Only positive voltages can be applied voltages = Box(-1, 1, shape=(3,), dtype=np.float64) # Positive and negative currents are possible currents = Box(-1, 1, shape=(3,), dtype=np.float64) _reset_action = [0, 0, 0] 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), ]
[docs] 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, ]
[docs] 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, ] return u_out
[docs] def set_action(self, action, t): # Docstring in base class times = [] times += self._subconverters[0].set_action([0.5 * (action[0] + 1)], t) times += self._subconverters[1].set_action([0.5 * (action[1] + 1)], t) times += self._subconverters[2].set_action([0.5 * (action[2] + 1)], t) return sorted(list(set(times)))
def _convert(self, i_in, t): # Not used pass
[docs] 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)])