Heating controller with neural thermal model written in Python
Jacek Kowalski
2018-06-24 425bf71fc0b24b547006686d83404c54b983de0b
commit | author | age
425bf7 1 import math
JK 2 import abc
3
4 from .SlidingWindow import SlidingWindow
5
6
7 class Cost(abc.ABC):
8     def __init__(self, temp_min=19.5, temp_max=20.6, params=None):
9         if params is None:
10             params = ('heating', 'heater_on', 'heater_off', 'discomfort')
11         self.params = params
12
13         self.costs = {}
14         for param in params:
15             self.costs[param] = 0.0
16
17         self.temp_min = temp_min
18         self.temp_max = temp_max
19
20     def total(self):
21         return sum(self.costs.values())
22
23     def __lt__(self, other):
24         return self.total() < other.total()
25
26     def __str__(self):
27         result = ''
28         for param in self.params:
29             result += '{}={} '.format(param, self.costs[param])
30         result += 'total={}'.format(self.total())
31         return result
32
33     @abc.abstractmethod
34     def compute_partial_cost(self, window: SlidingWindow) -> dict:
35         pass
36
37     def add_partial_cost(self, window: SlidingWindow) -> dict:
38         partial_cost = self.compute_partial_cost(window)
39         for k, v in partial_cost.items():
40             self.costs[k] += v
41         return partial_cost
42
43
44 class DeviationCost(Cost):
45     def __init__(self, *args, **kwargs):
46         super().__init__(*args, **kwargs, params=('step', 'diff', 'diff_sum', 'mean', 'sq_err'))
47
48     def compute_partial_cost(self, window: SlidingWindow) -> dict:
49         partial_cost = {}
50
51         temp = window.get_current_value('temp_in')
52         temp_calc = window.get_current_orig_value('temp_in')
53         diff = abs(temp - temp_calc)
54
55         partial_cost['diff_sum'] = diff
56
57         partial_cost['mean'] = diff * diff
58         partial_cost['step'] = 1
59
60         return partial_cost
61
62     def add_partial_cost(self, window: SlidingWindow) -> dict:
63         result = super().add_partial_cost(window)
64         self.costs['sq_err'] = self.costs['mean'] / self.costs['step']
65         self.costs['diff'] = max(self.costs['diff'], result['diff_sum'])
66         return result
67
68
69 class NegativeCostBase(Cost):
70     def compute_partial_cost(self, window: SlidingWindow):
71         partial_cost = {}
72         partial_cost['heating'] = window.get_previous_value('mode')
73
74         temperature = window.get_next_value('temp_in')
75         deviation = 0
76         if temperature < self.temp_min:
77             deviation = self.temp_min - temperature
78         elif temperature > self.temp_max:
79             deviation = temperature - self.temp_max
80         if deviation:
81             partial_cost['discomfort'] = pow(deviation * 11, 2)
82
83         return partial_cost
84
85
86 class NegativeCost(Cost):
87     def compute_partial_cost(self, window: SlidingWindow):
88         mode = window.get_previous_value('mode')
89         last_mode = window.get_value('mode', -3)
90         temperature = window.get_current_value('temp_in')
91
92         partial_cost = {}
93
94         if mode:
95             partial_cost['heating'] = 1
96
97         if last_mode is not None:
98             if not last_mode and mode:
99                 partial_cost['heater_on'] = 3
100             elif last_mode and not mode:
101                 partial_cost['heater_off'] = 4
102
103         deviation = 0
104         if temperature < self.temp_min:
105             deviation = self.temp_min - temperature
106         elif temperature > self.temp_max:
107             deviation = temperature - self.temp_max
108         if deviation:
109             partial_cost['discomfort'] = pow(deviation * 11, 2)
110
111         return partial_cost
112
113
114 class NegativeCostExtendedRange(NegativeCost):
115     def __init__(self):
116         super().__init__(temp_max=20.6)