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