def __init__(self, friendly_name, pwr, efficiency_factor, thermal_process, read_params, write_params): """ __init__(self, friendly_name, pwr, efficiency_factor, thermal_process, readparamlist, writeparamlist) This :class:`Actor` can either heat of cool a :class:`gridsim.thermal.core.ThermalProcess`. :param friendly_name: friendly name for the AbstractElectricalCPSElement :param pwr: power of the heater/cooler system :param efficiency_factor: efficiency factor of the system :param thermal_process: thermal process of the system :param read_params: read parameter of the actor :param write_params: write parameter of the actor """ super(ElectroThermalHeaterCooler, self).__init__(friendly_name) self.read_params = read_params self.write_params = write_params self._efficiency_factor = units.value(efficiency_factor) self._thermal_process = thermal_process self.power = units.value(pwr, units.watt) self._on = False
def __init__(self, friendly_name, mean_power, standard_deviation): """ __init__(self, friendly_name, mean_power, standard_deviation) This class provides a Consuming-Producing-Storing element having a random behavior. The consecutive consumed or produced power values are IID (Independently and Identically Distributed). The distribution is Gaussian. At initialization, the `mean_power` and 'standard_distribution' are provided beside the element `friendly_name`. If `mean_power` is positive, the element is in the mean a consumer. If it is negative, the element is in the mean a producer. :param friendly_name: Friendly name for the element. Should be unique within the simulation module. :type friendly_name: str :param mean_power: The mean value of the Gaussian distributed power. :type mean_power: power, see :mod:`gridsim.unit` :param standard_deviation: The standard deviation of the Gaussian distributed power. :type standard_deviation: power, see :mod:`gridsim.unit` """ # HACK: when object is constructed with *args or **kwargs if not isinstance(mean_power, (int, float)): mean_power = units.value(units.to_si(mean_power)) if not isinstance(standard_deviation, (int, float)): standard_deviation = units.value(units.to_si(standard_deviation)) super(GaussianRandomElectricalCPSElement, self).__init__(friendly_name) self._mean_power = mean_power self._standard_deviation = standard_deviation
def test_plot_recorder(self): recorder = PlotRecorder('power', units.minute, units.watt) recorder.on_simulation_reset(['foo', 'bar']) delta_time = units.value(1*units.minute, units.second) recorder.on_simulation_step(delta_time) recorder.on_observed_value('foo', delta_time, 14.2*units.watt) recorder.on_observed_value('bar', delta_time, 12*units.watt) delta_time = units.value(2*units.minute, units.second) recorder.on_simulation_step(delta_time) recorder.on_observed_value('foo', delta_time, 12*units.watt) recorder.on_observed_value('bar', delta_time, 12.1*units.watt) delta_time = units.value(3*units.minute, units.second) recorder.on_simulation_step(delta_time) recorder.on_observed_value('foo', delta_time, 16.1*units.watt) recorder.on_observed_value('bar', delta_time, 14.2*units.watt) delta_time = units.value(4*units.minute, units.second) recorder.on_simulation_step(delta_time) recorder.on_observed_value('foo', delta_time, 18.4*units.watt) recorder.on_observed_value('bar', delta_time, 9*units.watt) self.assertEqual(recorder.x_unit(), 'minute') self.assertEqual(recorder.x_values(), [1.0, 2.0, 3.0, 4.0]) self.assertDictEqual(recorder.y_values(), {'bar': [12, 12.1, 14.2, 9], 'foo': [14.2, 12, 16.1, 18.4]}) self.assertEqual(recorder.y_unit(), 'watt')
def __init__(self, friendly_name, k_factor, X, R=0): """ __init__(self, friendly_name, k_factor, X, R=0) Class for representing a transformer and/or a phase shifter in an electrical network. Its parameters are linked to the usual transformer graphical representation below:: 1:K R jX - - +-----------+ +-----------+ / / \ \\ o--->---| |---| |-------|---|--|---|------->---o +-----------+ +-----------+ \ \ / / - - While the transformer changes the voltage amplitude, the phase shifter changes the voltage phase. Accepting a complex ``K_factor`` parameter, the :class:`.ElectricalGenTransformer` is a common representation for a transformer and a phase shifter. At initialization, in addition to the line ``friendly_name``, the ``K-factor``, the reactance (``X``), and the resistance (``R``) have to be given. ``R`` defaults to zero. :param friendly_name: Friendly name for the transformer or phase shifter. Should be unique within the simulation module, i.e. different for example from the friendly name of a bus :type friendly_name: str :param k_factor: Transformer or phase shifter K-factor. :type k_factor: complex :param X: Transformer or phase shifter reactance. :type X: ohm, see :mod:`gridsim.unit` :param R: Transformer or phase shifter resistance. :type R: ohm, see :mod:`gridsim.unit` """ # HACK: when object is constructed with *args or **kwargs if not isinstance(X, (int, float)): X = units.value(units.to_si(X)) if not isinstance(R, (int, float)): R = units.value(units.to_si(R)) # super constructor needs units as it is a "public" function super(ElectricalGenTransformer, self).__init__(friendly_name, X * units.ohm, R * units.ohm) if k_factor == 0: raise RuntimeError( 'Transformer or phase shifter K-factor can not be null') self.k_factor = k_factor """
def __init__(self, friendly_name, k_factor, X, R=0): """ __init__(self, friendly_name, k_factor, X, R=0) Class for representing a transformer and/or a phase shifter in an electrical network. Its parameters are linked to the usual transformer graphical representation below:: 1:K R jX - - +-----------+ +-----------+ / / \ \\ o--->---| |---| |-------|---|--|---|------->---o +-----------+ +-----------+ \ \ / / - - While the transformer changes the voltage amplitude, the phase shifter changes the voltage phase. Accepting a complex ``K_factor`` parameter, the :class:`.ElectricalGenTransformer` is a common representation for a transformer and a phase shifter. At initialization, in addition to the line ``friendly_name``, the ``K-factor``, the reactance (``X``), and the resistance (``R``) have to be given. ``R`` defaults to zero. :param friendly_name: Friendly name for the transformer or phase shifter. Should be unique within the simulation module, i.e. different for example from the friendly name of a bus :type friendly_name: str :param k_factor: Transformer or phase shifter K-factor. :type k_factor: complex :param X: Transformer or phase shifter reactance. :type X: ohm, see :mod:`gridsim.unit` :param R: Transformer or phase shifter resistance. :type R: ohm, see :mod:`gridsim.unit` """ # HACK: when object is constructed with *args or **kwargs if not isinstance(X, (int, float)): X = units.value(units.to_si(X)) if not isinstance(R, (int, float)): R = units.value(units.to_si(R)) # super constructor needs units as it is a "public" function super(ElectricalGenTransformer, self).__init__(friendly_name, X*units.ohm, R*units.ohm) if k_factor == 0: raise RuntimeError( 'Transformer or phase shifter K-factor can not be null') self.k_factor = k_factor """
def __init__(self, friendly_name, pwr, efficiency_factor, thermal_process): super(ElectroThermalHeaterCooler, self).__init__(friendly_name) self._efficiency_factor = units.value(efficiency_factor) self._thermal_process = thermal_process self._power = units.value(pwr, units.watt) self._on = False """
def __init__(self, friendly_name, pwr, efficiency_factor, thermal_process): super(ElectroThermalHeaterCooler, self).__init__(friendly_name) self._efficiency_factor = units.value(efficiency_factor) self._thermal_process = thermal_process self.power = units.value(pwr, units.watt) self._on = False """
def test_value(self): m = 3000*units.metre g = 10*units.gram kg = 0.01*units.kg self.assertEqual(units.value(g), 10) self.assertEqual(units.value(kg), 0.01) self.assertEqual(units.value(kg*m), 30.) self.assertEqual(units.value(g, units.decagram), 1.) self.assertEqual(units.value(m, units.Mm), 0.003) # Megametre
def test_si(self): km = 56*units.kilometre kg = 0.12*units.kilogram kwh = 43.1*units.kilowatthour self.assertEqual(units.value(units.to_si(km)), 56000) self.assertEqual(units.value(units.to_si(kg)), 0.12) self.assertEqual(units.value(units.to_si(kg*km)), 6720) self.assertEqual(units.value(units.to_si(kwh)), 155160000) # joule self.assertRaises(AttributeError, self._value_si, 7) self.assertRaises(AttributeError, self._value_si, "85.2")
def __init__(self, friendly_name, start_energy, max_energy, power, read_params, write_params): """ __init__(self,friendly_name,start_energy,max_energy,power,read_params,write_params) This :class:`Actor` simulates the behavior of a battery. The charge and discharge is considered equal. The battery reach a max energy when the battery is full and the empty state when the battery is low. .. note :: On consumption, the power is considered positive. On injection, the power is considered negative. :param friendly_name: friendly name for the :class:`gridsim.core.AbstractSimulationElement` :param start_energy: amount of energy to start with :param max_energy: max energy of the battery, the battery will stop storing :param power: power rate for the storage and discharge :param read_params: read parameter for the actor :param write_params: write parameter for the actor """ # HACK: when object is constructed with *args or **kwargs if not isinstance(start_energy, (int, float)): start_energy = units.value(units.to_si(start_energy)) if not isinstance(max_energy, (int, float)): max_energy = units.value(units.to_si(max_energy)) if not isinstance(power, (int, float)): power = units.value(units.to_si(power)) Actor.__init__(self) AbstractSimulationElement.__init__(self, friendly_name) CyberPhysicalModuleListener.__init__(self) self.read_params = read_params self.write_params = write_params self._store = True self._over_load = False self._empty = False if start_energy < max_energy: self._energy = start_energy self.energy = start_energy else: self._energy = max_energy self.energy = max_energy self._max_energy = max_energy self._power = power # the simulation runs with second, this is the conversion factor self._energy_seconds_to_hours = 3600.0
def __init__(self, friendly_name, power): """ __init__(self, friendly_name, power) This class provides the simplest Consuming-Producing-Storing element having a constant behavior. At initialization, the consumed or produced constant `power` has to be provided beside the element `friendly_name`. Power is positive, if consumed, negative, if produced. With the 'calculate' method the energy consumed or produced during the simulation step is calculated from the constant power value. :param friendly_name: Friendly name for the element. Should be unique within the simulation module. :type friendly_name: str :param power: The constant consumed (if positive) or produced (if negative) power. :type power: power, see :mod:`gridsim.unit` """ # HACK: when object is constructed with *args or **kwargs if not isinstance(power, (int, float)): power = units.value(units.to_si(power)) super(ConstantElectricalCPSElement, self).__init__(friendly_name) self.power = power
def __init__(self, friendly_name, temperature, position=Position()): """ __init__(self, friendly_name, temperature, position=Position()) This is a special thermal process with an infinite thermal capacity which results that the temperature of the process is constant, independent how much energy is taken from or given to the process. :param friendly_name: Friendly name to give to the process. :type friendly_name: str, unicode :param temperature: The (constant) temperature in degrees celsius. :type temperature: temperature :param position: The position of the :class:`.ThermalProcess`. :type position: :class:`Position` """ # HACK: when object is constructed with *args or **kwargs if not isinstance(temperature, (int, float)): temperature = units.value(units.to_si(temperature)) # super constructor needs units as it is a "public" function super(ConstantTemperatureProcess, self).__init__( friendly_name, float('inf')*units.heat_capacity, temperature*units.kelvin, position=position)
def __init__(self, friendly_name, cycle_delta_time, power_values, cycle_start_time=0): """ __init__(self, friendly_name, cycle_delta_time, power_values, cycle_start_time=0*units.second) This class provides a cyclic Consuming-Producing-Storing element for which the consumed or produced power values may be updated. Update will take place at next cycle start. :param friendly_name: Friendly name for the element. Should be unique within the simulation module. :type friendly_name: str :param cycle_delta_time: cycle time resolution value in seconds. :type cycle_delta_time: int :param power_values: power values consumed or produced during a cycle. :type power_values: 1-D numpy array of float :param cycle_start_time: cycle start time in seconds. Defaults to 0. :type cycle_start_time: int """ # HACK: when object is constructed with *args or **kwargs if power_values.dtype is not (int, float): power_values = units.value(units.to_si(power_values)) super(UpdatableCyclicElectricalCPSElement, self).\ __init__(friendly_name, cycle_delta_time, power_values, cycle_start_time) self._new_power_values = power_values self._update_done = True
def calculate(self, time, delta_time): self._time_series.set_time(time) unit_delta_time = delta_time volume = units.to_si(units(self._time_series.volume, units.litre))*delta_time/units.value(self._time_converter(1), units.second) # thermal losses when used [W/K] on_losses = units.value(volume)*units.value(Water().weight)*units.value(Water().thermal_capacity)/unit_delta_time # total thermal losses [W/K] losses = self._off_losses + on_losses self._temperature = self._temperature + \ ((unit_delta_time*losses/self._cb)*(self._temperature_in-self._temperature)) +\ (unit_delta_time/self._cb)*self.power
def test_simulation_time(self): total_time = 1*units.hour delta_time = 1*units.second sim = Simulator() el = sim.time_test.add(TimeTestElement('test')) sim.reset() sim.run(total_time, delta_time) self.assertEqual(el.val, units.value(total_time, units.second)) self.assertEqual(el.iter, 3601) # from 0 to 3601 included
def __init__(self, friendly_name, X, R=0 * units.ohm): """ __init__(self, friendly_name, X, R=0*units.ohm) This class is the base for all electrical element that can be placed on a network branch, e.g. transmission lines, transformers, phase shifters,... It is based on the general :class:`.AbstractElectricalElement` class. At initialization the user has to give the two-port ``friendly_name``. :param friendly_name: Friendly name for the element. Should be unique within the simulation module. :type friendly_name: str :param X: reactance of the element :type X: ohm, see :mod:`gridsim.unit` :param R: resistance of the element :type R: ohm, see :mod:`gridsim.unit` """ # HACK: when object is constructed with *args or **kwargs if not isinstance(X, (int, float)): X = units.value(units.to_si(X)) if not isinstance(R, (int, float)): R = units.value(units.to_si(R)) super(AbstractElectricalTwoPort, self).__init__(friendly_name, ) if X <= 0: raise RuntimeError('Line reactance X cannot be negative or null') if R < 0: raise RuntimeError('Line resistance R can not be negative number') self.X = X """ The reactance. """ self.R = R """
def __init__(self, friendly_name, X, R=0*units.ohm): """ __init__(self, friendly_name, X, R=0*units.ohm) This class is the base for all electrical element that can be placed on a network branch, e.g. transmission lines, transformers, phase shifters,... It is based on the general :class:`.AbstractElectricalElement` class. At initialization the user has to give the two-port ``friendly_name``. :param friendly_name: Friendly name for the element. Should be unique within the simulation module. :type friendly_name: str :param X: reactance of the element :type X: ohm, see :mod:`gridsim.unit` :param R: resistance of the element :type R: ohm, see :mod:`gridsim.unit` """ # HACK: when object is constructed with *args or **kwargs if not isinstance(X, (int, float)): X = units.value(units.to_si(X)) if not isinstance(R, (int, float)): R = units.value(units.to_si(R)) super(AbstractElectricalTwoPort, self).__init__(friendly_name, ) if X <= 0: raise RuntimeError('Line reactance X cannot be negative or null') if R < 0: raise RuntimeError('Line resistance R can not be negative number') self.X = X """ The reactance. """ self.R = R """
def calculate(self, time, delta_time): """ Calculates the energy consumed or produced by the element during the simulation step. :param time: The actual time of the simulator in seconds. :type time: time, see :mod:`gridsim.unit` :param delta_time: The delta time for which the calculation has to be done in seconds. :type delta_time: time, see :mod:`gridsim.unit` """ self._time_series.set_time(time) self._internal_delta_energy = units.value(self._time_series.power) * delta_time
def calculate(self, time, delta_time): self._time_series.set_time(time) unit_delta_time = delta_time if self._time_converter == None: volume = units.to_si(units(self._time_series.volume, units.litre)) * delta_time else: volume = units.to_si( units(self._time_series.volume, units.litre)) * delta_time / units.value( self._time_converter(1), units.second) # thermal losses when used [W/K] on_losses = units.value(volume) * units.value( Water().weight) * units.value( Water().thermal_capacity) / unit_delta_time # total thermal losses [W/K] losses = self._off_losses + on_losses self._temperature = self._temperature + \ ((unit_delta_time * losses / self._cb) * (self._temperature_in - self._temperature)) + \ (unit_delta_time / self._cb) * self.power
def calculate(self, time, delta_time): """ calculate(self, time, delta_time) Calculates the temperature of the element during the simulation step. :param time: The actual time of the simulator. :type time: float in second :param delta_time: The delta time for which the calculation has to be done. :type delta_time: float in second .. seealso:: :func:`gridsim.timeseries.TimeSeriesObject.set_time` """ self._time_series.set_time(time) # the parent (ThermalProcess) already has a 'temperature' in # its local params self.temperature = units.value(self._time_series.temperature)
def __init__(self, friendly_name, cycle_delta_time, power_values, cycle_start_time=0): """ __init__(self, friendly_name, cycle_delta_time, power_values, cycle_start_time=0) This class provides a Consuming-Producing-Storing element having a cyclic behavior. At initialization, beside the element `friendly_name`, the cycle time resolution `cycle_delta_time`, the cycle sequence of `power_values`, and the `cycle_start_time` have to be given. :param friendly_name: Friendly name for the element. Should be unique within the simulation module. :type friendly_name: str :param cycle_delta_time: cycle time resolution value in seconds. :type cycle_delta_time: int :param power_values: power values consumed or produced during a cycle. :type power_values: 1-D numpy array of power :param cycle_start_time: cycle start time in seconds. Defaults to 0. :type cycle_start_time: int """ super(CyclicElectricalCPSElement, self).__init__(friendly_name) # HACK: when object is constructed with *args or **kwargs if power_values.dtype is not (int, float): power_values = units.value(units.to_si(power_values)) if power_values.dtype != float: raise TypeError("'power_values' has to be an array of floats.") if len(power_values.shape) != 1: raise RuntimeError( "'power_values' has to be a one-dimensional array") self._cycle_delta_time = cycle_delta_time self._power_values = power_values self._cycle_length = len(power_values) self._cycle_start_time = cycle_start_time
def __getattr__(self, item): return units.value(getattr(self._time_series, item))
def _value_si(self, a): return units.value(units.to_si(a))
def __init__(self, friendly_name, length, X, R=0, B=0): """ __init__(self, friendly_name, length, X, R=0, B=0) Class for representing a transmission line in an electrical network. Its parameters are linked to the transmission line PI model represented graphically below:: R jX +-----------+ +-----------+ o--->---o-------| |---| |-------o--->---o | +-----------+ +-----------+ | | | ----- jB/2 ----- jB/2 ----- ----- | | | | --- --- At initialization, in addition to the line ``friendly_name``, the line length, the line reactance (``X``), line resistance (``R``) and line charging (B) have to be given. ``R`` and ``B`` default to zero. :param friendly_name: Friendly name for the line. Should be unique within the simulation module, i.e. different for example from the friendly name of a bus. :type friendly_name: str :param length: Line length. :type length: length, see :mod:`gridsim.unit` :param X: Line reactance. :type X: ohm, see :mod:`gridsim.unit` :param R: Line resistance. :type R: ohm, see :mod:`gridsim.unit` :param B: Line charging. :type B: siemens, see :mod:`gridsim.unit` """ # HACK: when object is constructed with *args or **kwargs if not isinstance(length, (int, float)): length = units.value(units.to_si(length)) if not isinstance(X, (int, float)): X = units.value(units.to_si(X)) if not isinstance(R, (int, float)): R = units.value(units.to_si(R)) if not isinstance(B, (int, float)): B = units.value(units.to_si(B)) # super constructor needs units as it is a "public" function super(ElectricalTransmissionLine, self).__init__(friendly_name, X*units.ohm, R*units.ohm) if length <= 0: raise RuntimeError('Length has to be a strictly positive number') if B < 0: raise RuntimeError('Line charging B can not be negative number') self.length = length """ The transmission line length. """ self.B = B """
def __init__(self, friendly_name, thermal_capacity, initial_temperature, mass=1, position=Position()): """ __init__(self, friendly_name, thermal_capacity, initial_temperature, mass=1*units.kilogram, position=Position()): The very basic element of a thermal simulation. A thermal process represents a closed thermal envelope like a room or a amount of matter which has an uniform thermal capacity and and stores an amount of thermal energy resulting in a temperature. Those thermal processes can be coupled by :class:`ThermalCoupling` element. :param friendly_name: The name to give to the thermal process. :type friendly_name: str :param thermal_capacity: The thermal capacity of the process. See :class:`.Material`. :type thermal_capacity: heat_capacity, see :mod:`gridsim.unit` :param initial_temperature: The initial temperature of the process in degrees. :type initial_temperature: kelvin, see :mod:`gridsim.unit` :param mass: the mass of the element :type mass: mass, see :mod:`gridsim.unit` :param position: The position of the process. :type position: :class:`.Position` """ # HACK: when object is constructed with *args or **kwargs if not isinstance(thermal_capacity, (int, float)): thermal_capacity = units.value(units.to_si(thermal_capacity)) if not isinstance(initial_temperature, (int, float)): initial_temperature = units.value(units.to_si(initial_temperature)) if not isinstance(mass, (int, float)): mass = units.value(units.to_si(mass)) super(ThermalProcess, self).__init__(friendly_name, position) self._initial_temperature = initial_temperature """ The initial temperature of the process. Need for reset. """ self._mass = mass """ The mass of the thermal process. """ self._thermal_capacity = thermal_capacity """ The thermal capacity of the thermal process. """ self.temperature = self._initial_temperature """ The temperature of the process. """ self._internal_thermal_energy = self._initial_temperature * \ self._thermal_capacity * self._mass """ The internal thermal energy stored inside the thermal process. """ self.thermal_energy = self._internal_thermal_energy """
def __init__(self, friendly_name, thermal_conductivity, from_process, to_process, contact_area=1, thickness=1): """ __init__(self, friendly_name, thermal_conductivity, from_process, to_process, contact_area=1*units.metre**2, thickness=1*units.metre) A thermal coupling connects two thermal processes allowing them to exchange thermal energy. :param friendly_name: The friendly name to identify the element. :type friendly_name: str :param thermal_conductivity: The thermal conductivity of the thermal element. :type thermal_conductivity: thermal conductivity, see :mod:`gridsim.unit` :param from_process: The first process coupled process. :type from_process: :class:`ThermalProcess` :param to_process: The second process coupled process. :type to_process: :class:`ThermalProcess` :param contact_area: The size of the contact area process. :type contact_area: square_meter, see :mod:`gridsim.unit` :param thickness: the thickness of the contact area process. :type thickness: meter, see :mod:`gridsim.unit` """ # HACK: when object is constructed with *args or **kwargs if not isinstance(thermal_conductivity, (int, float)): thermal_conductivity = units.value(units.to_si(thermal_conductivity)) if not isinstance(contact_area, (int, float)): contact_area = units.value(units.to_si(contact_area)) if not isinstance(thickness, (int, float)): thickness = units.value(units.to_si(thickness)) super(ThermalCoupling, self).__init__(friendly_name) self.from_process = from_process self.to_process = to_process self.thermal_conductivity = thermal_conductivity """ The thermal conductivity of the coupling in W/K. """ self._contact_area = contact_area """ The size of the contact area between the two :class:`ThermalProcess` """ self._thickness = thickness """ The thickness of the material between the two :class:`ThermalProcess` """ self._delta_energy = 0 """ The energy variation """ self.power = None """
def __init__(self, friendly_name, target_temperature, hysteresis, thermal_process, subject, attribute, on_value=True, off_value=False, position=Position()): """ A thermostat controller. This class measures the temperature of a thermal process (typically a room) and controls ANY attribute of any AbstractSimulationElement depending the measured temperature, the given target_temperature and the hysteresis. :param: friendly_name: User friendly name to give to the element. :type friendly_name: str :param: target_temperature: The temperature to try to maintain inside the target ThermalProcess. :type: target_temperature: temperature see :mod:`gridsim.unit` :param: hysteresis: The +- hysteresis in order to avoid to fast on/off switching. :type: hysteresis: delta temperature see :mod:`gridsim.unit` :param: thermal_process: The reference to the thermal process to observe. :type: thermal_process: :class:`.ThermalProcess` :param: subject: Reference to the object of which is attribute has to be changed depending on the temperature. :type: object :param: attribute: The name of the attribute to control as string. :type: str :param: on_value: The value to set for the attribute in order to turn the device "on". :type: on_value: any :param: off_on_value: The value to set for the attribute in order to turn the device "off". :type: off_value: any :param position: The position of the thermal element. Defaults to [0,0,0]. :type position: :class:`Position` """ super(Thermostat, self).__init__(friendly_name, position) self.target_temperature = units.value(target_temperature, units.kelvin) """ The temperature to try to retain inside the observer thermal process by conducting an electrothermal element. """ self.hysteresis = units.value(hysteresis, units.kelvin) """ The +- hysteresis applied to the temperature measure in order to avoid to fast on/off switching. """ if not hasattr(thermal_process, 'temperature'): raise TypeError('thermal_process') self.thermal_process = thermal_process """ The reference to the thermal process to observe and read the temperature from. """ self.subject = subject """ The reference to the element to control. """ self.attribute = attribute """ Name of the attribute to control. """ self.on_value = on_value """ Value to set in order to turn the element on. """ self.off_value = off_value """ Value to set in order to turn the element off. """ self._output_value = off_value
def __init__(self, friendly_name, fname_or_power_values, frequencies=None): """ __init__(self, friendly_name, fname_or_power_values, frequencies=None) This class provides a Consuming-Producing-Storing element having a random behavior. The consecutive consumed or produced power values are IID (Independently and Identically Distributed). The distribution, discrete and finite, is given as parameter. Beside the element ``friendly_name``, the constructor parameters are either the name of the file the distribution has to be read from, either the potentially consumed or produced ``power_values``,together with their ``frequencies`` or probabilities. Input ``power values`` has to be a monotonically increasing sequence of float. Input 'frequencies' can be either integers (number of occurrences), or floats summing to 1.0 (relative frequencies or probabilities), or monotonically increasing sequence of positive floats ending with 1.0 (cumulative relative frequencies or probabilities) :param friendly_name: Friendly name for the element. Should be unique within the simulation module. :type friendly_name: str :param fname_or_power_values: Name of the file from which the distribution has to be read or Power values which may be consumed (positive) or produced (negative), ordered in a monotonically increasing sequence. :type fname_or_power_values: either string or 1-D numpy array of float :param frequencies: Number of occurrences (frequencies), or relative frequencies, or cumulative relative frequencies of corresponding power values, if those are given as second parameter, None if filename is given as 2nd parameter :type frequencies: None or 1-D numpy array of integer or float """ # HACK: when object is constructed with *args or **kwargs if fname_or_power_values.dtype is not (int, float): fname_or_power_values = units.value(units.to_si(fname_or_power_values)) super(AnyIIDRandomElectricalCPSElement, self).__init__(friendly_name) # if first parameter is a string (name of a file), read data if isinstance(fname_or_power_values, str): if not frequencies is None: raise RuntimeError( "'frequencies' cannot be passed as argument, they are read " "from file with name '" + fname_or_power_values + "' in this case") [power_values, frequencies] = self._read_hist_from_file( fname_or_power_values) else: power_values = fname_or_power_values # just copy variable # check if power_values.dtype != float: raise TypeError("'power_values' has to be an array of floats.") if len(power_values.shape) != 1: raise RuntimeError( "'power_values' has to be a one-dimensional array") if not frequencies.dtype in (int, float): raise TypeError( "'frequencies' has to be an array of integers or floats.") if len(frequencies.shape) != 1: raise RuntimeError( "'frequencies' has to be a one-dimensional array") if frequencies.shape[0] != power_values.shape[0]: raise RuntimeError( "'frequencies' and 'power_values' must have the same length") self._power_values = power_values if frequencies[-1] == 1.0: for i_pos in range(1, frequencies.shape[0]): if frequencies[i_pos] <= frequencies[i_pos - 1]: raise RuntimeError( "cumulative relative 'frequencies' should be " "monotonically increasing.") self._cdf = frequencies else: if frequencies.dtype == int: frequencies.astype('float') sum_freq = sum(frequencies) if sum_freq == 0.: raise TypeError( "sum of values in 'frequencies' may not be zero.") self._cdf = np.cumsum((1.0 / sum_freq) * frequencies)
def __init__(self, friendly_name, length, X, R=0, B=0): """ __init__(self, friendly_name, length, X, R=0, B=0) Class for representing a transmission line in an electrical network. Its parameters are linked to the transmission line PI model represented graphically below:: R jX +-----------+ +-----------+ o--->---o-------| |---| |-------o--->---o | +-----------+ +-----------+ | | | ----- jB/2 ----- jB/2 ----- ----- | | | | --- --- At initialization, in addition to the line ``friendly_name``, the line length, the line reactance (``X``), line resistance (``R``) and line charging (B) have to be given. ``R`` and ``B`` default to zero. :param friendly_name: Friendly name for the line. Should be unique within the simulation module, i.e. different for example from the friendly name of a bus. :type friendly_name: str :param length: Line length. :type length: length, see :mod:`gridsim.unit` :param X: Line reactance. :type X: ohm, see :mod:`gridsim.unit` :param R: Line resistance. :type R: ohm, see :mod:`gridsim.unit` :param B: Line charging. :type B: siemens, see :mod:`gridsim.unit` """ # HACK: when object is constructed with *args or **kwargs if not isinstance(length, (int, float)): length = units.value(units.to_si(length)) if not isinstance(X, (int, float)): X = units.value(units.to_si(X)) if not isinstance(R, (int, float)): R = units.value(units.to_si(R)) if not isinstance(B, (int, float)): B = units.value(units.to_si(B)) # super constructor needs units as it is a "public" function super(ElectricalTransmissionLine, self).__init__(friendly_name, X * units.ohm, R * units.ohm) if length <= 0: raise RuntimeError('Length has to be a strictly positive number') if B < 0: raise RuntimeError('Line charging B can not be negative number') self.length = length """ The transmission line length. """ self.B = B """
def __getattr__(self, item): # this function is not called when using thermalprocess.temperature # because its parent (ThermalProcess) already has a 'temperature' return units.value(getattr(self._time_series, item))
def __init__(self, friendly_name, height, radius, thickness, initial_temperature, heat_transfer_coeff, power, temperature_in, time_series, readparamlist, writeparamlist, time_converter=None): """ :param friendly_name: Friendly name to give to the process. :type friendly_name: str, unicode :param height: the height of the boiler :type height: units.metre :param radius: the radius of the boiler :type radius: units.metre :param thickness: the thickness of the boiler :type thickness: units.metre :param initial_temperature: the initial temperature of the water in the boiler. :type initial_temperature: units.kelvin :param heat_transfer_coeff: the heat transfer coefficient :type heat_transfer_coeff: units.watt/(units.kelvin*(units.meter**2) :param power: the electrical power to heat the boiler :type power: units.watt :param temperature_in: the temperature of the input water :type temperature_in: units.kelvin :param time_series: the time_series to load the stream :type time_series: class:`gridsim.timeseries.TimeSeries` :param readparamlist: read parameter of the actor :param writeparamlist: write parameter of the actor :param time_converter: :type time_converter: types.FunctionType or ``None`` :return: """ # HACK: when object is constructed with *args or **kwargs if not isinstance(height, (int, float)): height = units.value(units.to_si(height)) if not isinstance(radius, (int, float)): radius = units.value(units.to_si(radius)) if not isinstance(thickness, (int, float)): thickness = units.value(units.to_si(thickness)) if not isinstance(initial_temperature, (int, float)): initial_temperature = units.value(units.to_si(initial_temperature)) if not isinstance(heat_transfer_coeff, (int, float)): heat_transfer_coeff = units.value(units.to_si(heat_transfer_coeff)) if not isinstance(temperature_in, (int, float)): temperature_in = units.value(units.to_si(temperature_in)) if not isinstance(power, (int, float)): power = units.value(units.to_si(power)) super(Boiler, self). \ __init__(friendly_name) self.readparamtype = readparamlist self.writeparamtype = writeparamlist self._time_converter = time_converter self._time_series = time_series self._time_series.load(time_converter=time_converter) self._height = height self._radius = radius self._thickness = thickness self._initial_temperature = initial_temperature self._temperature = self._initial_temperature self._heat_transfer_coeff = heat_transfer_coeff self._power = power self.old_power = 0 self._temperature_in = temperature_in # potential energy [J/K] self._cb = units.value(Water().thermal_capacity) * \ units.value(Water().weight) * math.pi * self._height * (self._radius ** 2) # global loss factor [W/K.m2] self._ub = 1 / ((1 / self._heat_transfer_coeff) + (self._thickness / units.value(BoilerMaterial().thermal_conductivity))) # thermal losses when off [W/K] self._off_losses = self._ub * ( (2. * math.pi * (self._radius**2)) + (2 * math.pi * self._height * self._radius)) self._on = False
def __init__(self, friendly_name, thermal_conductivity, from_process, to_process, contact_area=1, thickness=1): """ __init__(self, friendly_name, thermal_conductivity, from_process, to_process, contact_area=1*units.metre**2, thickness=1*units.metre) A thermal coupling connects two thermal processes allowing them to exchange thermal energy. :param friendly_name: The friendly name to identify the element. :type friendly_name: str :param thermal_conductivity: The thermal conductivity of the thermal element. :type thermal_conductivity: thermal conductivity, see :mod:`gridsim.unit` :param from_process: The first process coupled process. :type from_process: :class:`ThermalProcess` :param to_process: The second process coupled process. :type to_process: :class:`ThermalProcess` :param contact_area: The size of the contact area process. :type contact_area: square_meter, see :mod:`gridsim.unit` :param thickness: the thickness of the contact area process. :type thickness: meter, see :mod:`gridsim.unit` """ # HACK: when object is constructed with *args or **kwargs if not isinstance(thermal_conductivity, (int, float)): thermal_conductivity = units.value( units.to_si(thermal_conductivity)) if not isinstance(contact_area, (int, float)): contact_area = units.value(units.to_si(contact_area)) if not isinstance(thickness, (int, float)): thickness = units.value(units.to_si(thickness)) super(ThermalCoupling, self).__init__(friendly_name) self.from_process = from_process self.to_process = to_process self.thermal_conductivity = thermal_conductivity """ The thermal conductivity of the coupling in W/K. """ self._contact_area = contact_area """ The size of the contact area between the two :class:`ThermalProcess` """ self._thickness = thickness """ The thickness of the material between the two :class:`ThermalProcess` """ self._delta_energy = 0 """ The energy variation """ self.power = None """
def __init__(self, friendly_name, height, radius, thickness, initial_temperature, heat_transfer_coeff, power, temperature_in, time_series, time_converter=None): """ :param friendly_name:Friendly name to give to the process. :type friendly_name: str, unicode :param height: the height of the boiler :type height: units.metre :param radius: the radius of the boiler :type radius: units.metre :param thickness: the thickness of the boiler :type thickness: units.metre :param initial_temperature: the initial temperature of the water in the boiler. :type initial_temperature: units.kelvin :param heat_transfer_coeff: the heat transfer coefficient :type heat_transfer_coeff: units.watt/(units.kelvin*(units.meter**2) :param power: the electrical power to heat the boiler :type power: units.watt :param temperature_in: the temperature of the input water :type temperature_in: units.kelvin :param time_series: the time_series to load the stream :type time_series: class:`gridsim.timeseries.TimeSeries` :param time_converter: :type time_converter: types.FunctionType or ``None`` :return: """ # HACK: when object is constructed with *args or **kwargs if not isinstance(height, (int, float)): height = units.value(units.to_si(height)) if not isinstance(radius, (int, float)): radius = units.value(units.to_si(radius)) if not isinstance(thickness, (int, float)): thickness = units.value(units.to_si(thickness)) if not isinstance(initial_temperature, (int, float)): initial_temperature = units.value(units.to_si(initial_temperature)) if not isinstance(heat_transfer_coeff, (int, float)): heat_transfer_coeff = units.value(units.to_si(heat_transfer_coeff)) if not isinstance(temperature_in, (int, float)): temperature_in = units.value(units.to_si(temperature_in)) if not isinstance(power, (int, float)): power = units.value(units.to_si(power)) super(Boiler, self).\ __init__(friendly_name) self._time_converter = time_converter self._time_series = time_series self._time_series.load(time_converter=time_converter) self._height = height self._radius = radius self._thickness = thickness self._initial_temperature = initial_temperature self._temperature = self._initial_temperature self._heat_transfer_coeff = heat_transfer_coeff self._power = power self.old_power = 0 self._temperature_in = temperature_in # potential energy [J/K] self._cb = units.value(Water().thermal_capacity) * \ units.value(Water().weight)*math.pi*self._height*(self._radius**2) # global loss factor [W/K.m2] self._ub = 1/((1/self._heat_transfer_coeff) + (self._thickness/units.value(BoilerMaterial().thermal_conductivity))) # thermal losses when off [W/K] self._off_losses = self._ub * ((2.*math.pi*(self._radius**2)) + (2*math.pi*self._height*self._radius)) self._on = False