# coding=utf8 """ This file contains classes for simulating, controlling and observing a fridge. @author: Stefan Scherfke @contact: stefan.scherfke at uni-oldenburg.de """ from math import exp import logging import random from SimPy.Simulation import Process, Simulation, \ activate, hold, initialize, now, simulate log = logging.getLogger('Processes') class Fridge(Process): """ This class represents a simulated fridge. It's temperature T for and equidistant series of time steps is computed by $T_{i+1} = \epsilon \cdot T_i + (1 - \epsilon) \cdot \left(T^O - \eta \cdot \frac{q_i}{A}\right)$ with $\epsilon = e^{-\frac{\tau A}{m_c}}$. """ def __init__(self, sim, T_O = 20.0, A = 3.21, m_c = 15.97, tau = 1.0/60, eta = 3.0, q_i = 0.0, q_max = 70.0, T_i = 5.0, T_range = [5.0, 8.0], noise = False): """ Init all required variables. @param sim: The SimPy simulation this process belongs to @type sim: SimPy.Simulation @param T_O: Outside temperature @param A: Insulation @param m_c: Thermal mass/thermal storage capacity @param tau: Time span between t_i and t_{i+1} @param eta: Efficiency of the cooling device @param q_i: Initial/current electrical power @param q_max: Power required during cool-down @param T_i: Initial/current temperature @param T_range: Allowed range for T_i @param noise: Add noise to the fridge's parameters, if True @type noise: bool """ Process.__init__(self, sim = sim) self.T_O = T_O self.A = A self.m_c = random.normalvariate(20, 4.5) if noise else m_c self.tau = tau self.eta = eta self.q_i = q_i self.q_max = q_max self.T_i = random.uniform(T_range[0], T_range[1]) if noise else T_i self.T_range = T_range def run(self): """ Calculate the fridge's temperature for the current time step. """ while True: epsilon = exp(-(self.tau * self.A) / self.m_c) self.T_i = epsilon * self.T_i + (1 - epsilon) \ * (self.T_O - self.eta * (self.q_i / self.A)) if self.T_i >= self.T_range[1]: self.q_i = self.q_max # Cool down elif self.T_i <= self.T_range[0]: self.q_i = 0.0 # Stop cooling log.debug('T_i: %2.2f°C at %.2f' % (self.T_i, self.sim.now())) yield hold, self, self.tau def coolDown(self): """ Start cooling down now! """ self.q_i = self.q_max class FridgeObserver(Process): """ This process observes the temperature and power consumption of a set of fridges. """ def __init__(self, sim, fridges, tau, aggSteps): """ Init the observer. @param sim: The SimPy simulation this process belongs to @type sim: SimPy.Simulation @param fridges: A list of fridges to be observed @type fridges: tuple of Fridge @param tau: Time interval for observations @type tau: float @param aggSteps: Specifies after how many timesteps tau the collected data is aggregated and stored. @type aggSteps: int """ Process.__init__(self, sim = sim) self._fridges = fridges self._tau = tau self._aggSteps = aggSteps self._data = [] def run(self): """ Start observation """ aggSteps = 0 consumption = 0 lastProgUpdate = 0 while True: prog = self.sim.now() * 100 / self.sim._endtime if int(prog) > lastProgUpdate: log.info('Progress: %d%%' % prog) lastProgUpdate = prog if (aggSteps >= self._aggSteps): log.debug('Aggregating at %.2f' % self.sim.now()) self._data.append(consumption/self._aggSteps) consumption = 0 aggSteps = 0 for fridge in self._fridges: consumption += fridge.q_i aggSteps += 1 yield hold, self, self._tau def getData(self): """ Return the collected data @return: a list with the collected data """ return self._data if __name__ == '__main__': logging.basicConfig( level = logging.DEBUG, format = '%(levelname)-8s %(asctime)s %(name)s: %(message)s') tau = 1./60 # Step size 1min aggSteps = 15 # Aggregate consumption in 15min blocks params = {'tau': tau} sim = Simulation() fridge = Fridge(sim, **params) observer = FridgeObserver(sim, [fridge], tau, aggSteps) sim.activate(fridge, fridge.run(), at = 0) sim.activate(observer, observer.run(), at = 0) sim.simulate(until = 4 + tau) print observer.getData()