rufimelo commited on
Commit
b510d23
·
1 Parent(s): 0d10100

intiial commit

Browse files
.DS_Store ADDED
Binary file (6.15 kB). View file
 
Dockerfile ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10
2
+ # Set up a new user named "user" with user ID 1000
3
+ RUN useradd -m -u 1000 user
4
+
5
+ # Switch to the "user" user
6
+ USER user
7
+ # Set home to the user's home directory
8
+ ENV HOME=/home/user \
9
+ PATH=/home/user/.local/bin:$PATH
10
+
11
+ # Set the working directory to the user's home directory
12
+ WORKDIR $HOME/app
13
+
14
+ # Copy the current directory contents into the container at $HOME/app setting the owner to the user
15
+ COPY --chown=user ./app $HOME/app
16
+
17
+
18
+ ENV DASH_DEBUG_MODE False
19
+ #COPY ./app /app
20
+ #WORKDIR /app
21
+ RUN set -ex && \
22
+ pip install -r requirements.txt
23
+ EXPOSE 8050
24
+ CMD ["gunicorn", "-b", "0.0.0.0:8050", "--reload", "app:server"]
Dockerfile.dev ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10
2
+
3
+ ENV DASH_DEBUG_MODE True
4
+ COPY ./app /app
5
+ WORKDIR /app
6
+ RUN set -ex && \
7
+ pip install -r requirements.txt
8
+ EXPOSE 8050
9
+ CMD ["python", "app.py"]
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Jucy Technologies, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md CHANGED
@@ -1,11 +1,50 @@
1
  ---
2
- title: Smart Shower ATC
3
- emoji: 📊
4
- colorFrom: pink
5
  colorTo: gray
6
  sdk: docker
7
- pinned: false
8
- license: mit
9
  ---
 
10
 
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Smart Shower Dashboard
3
+ emoji: 📈
4
+ colorFrom: purple
5
  colorTo: gray
6
  sdk: docker
7
+ app_port: 8050
 
8
  ---
9
+ # Docker Dash (Plotly)
10
 
11
+ Dockerize a Python Dash app for quick prototyping.
12
+
13
+ ## Build and run
14
+
15
+ `prod` version is served by `gunicorn` instead of the `flask` dev server.
16
+
17
+ ```sh
18
+ # dev
19
+ docker build -f Dockerfile.dev -t docker-dash-example-dev .
20
+ docker run -p 8050:8050 -v "$(pwd)"/app:/app --rm docker-dash-example-dev
21
+
22
+ # prod
23
+ docker build -f Dockerfile -t docker-dash-example-prod .
24
+ docker run -p 8050:8050 -v "$(pwd)"/app:/app --rm docker-dash-example-prod
25
+ ```
26
+
27
+ ## Access the page
28
+
29
+ Go to `http://localhost:8050` in browser.
30
+
31
+ ## Switch debug mode in Dockerfile
32
+
33
+ ```dockerfile
34
+ ENV DASH_DEBUG_MODE True # False
35
+ ```
36
+
37
+ ## Development
38
+
39
+ Install the app requirements for development to get better editor support.
40
+
41
+ ```sh
42
+ poetry install
43
+ ```
44
+
45
+ Optional: clean initialization of `poetry`:
46
+
47
+ ```sh
48
+ poetry init
49
+ cat app/requirements.txt | xargs poetry add
50
+ ```
app/.DS_Store ADDED
Binary file (6.15 kB). View file
 
app/__pycache__/branch_and_bound.cpython-310.pyc ADDED
Binary file (6.23 kB). View file
 
app/__pycache__/core.cpython-310.pyc ADDED
Binary file (1.79 kB). View file
 
app/__pycache__/example_data.cpython-310.pyc ADDED
Binary file (2.42 kB). View file
 
app/app.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import plotly.express as px
3
+ import plotly.graph_objects as go
4
+ import pandas as pd
5
+ from dash import Dash, html, dcc, Input, Output, callback
6
+ import plotly.express as px
7
+ import numpy as np
8
+ import example_data
9
+ import core
10
+
11
+
12
+ outside_temp = example_data.ExampleDailyOutsideTemperature
13
+ energy_price = example_data.ExampleDailyEnergyCost
14
+ boiler_temperature = example_data.ExampleBoilerTemperature
15
+
16
+
17
+
18
+ data = pd.DataFrame(columns=['hour', 'energy_consumption', 'confort', 'policy_readable'])
19
+ data = pd.concat([data, pd.DataFrame({'hour': np.arange(0, 24), 'energy_consumption': energy_price.value, 'confort': np.random.rand(24),
20
+ 'policy_readable': np.random.choice(['A', 'B', 'C', 'D', 'E'], 24)
21
+ })])
22
+ debug = False
23
+
24
+
25
+ external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
26
+
27
+ app = Dash(__name__, external_stylesheets=external_stylesheets)
28
+
29
+ app.layout = html.Div([
30
+ dcc.Location(id='url', refresh=False),
31
+ html.Div(id='page-content')
32
+ ])
33
+
34
+ server = app.server
35
+
36
+
37
+ # SOlution options
38
+ solution_options = [
39
+ "Discrete Optimization",
40
+ "Continuous Optimization",
41
+ "Reinforcement Learning"
42
+ ]
43
+ solution_options_default_value = solution_options[0]
44
+
45
+ # provide a scalar value to enable the slider to select ideal temperature
46
+ ideal_temperature = 50
47
+
48
+ dashboard_layout = html.Div([
49
+ dcc.Link('About this project', href='/wiki'),
50
+
51
+ html.H1('System Evaluation'),
52
+ #small subtitle that says of solution is possible or not
53
+ html.Div(id='solution-status', children='', style={'color': 'lighrgrey'}),
54
+ html.Div([
55
+ html.Div([
56
+ html.H3('Solution'),
57
+ dcc.Dropdown(
58
+ id='solution-dropdown',
59
+ options=solution_options,
60
+ value=solution_options_default_value,
61
+ )
62
+ ], className='three columns'),
63
+ html.Div([
64
+ html.H3('Ideal Shower Temperature'),
65
+ dcc.Slider(
66
+ id='ideal-temperature-slider',
67
+ min=0,
68
+ max=100,
69
+ step=1,
70
+ value=ideal_temperature,
71
+ marks={
72
+ 0: '0°C',
73
+ 25: '25°C',
74
+ 50: '50°C',
75
+ 75: '75°C',
76
+ 100: '100°C'
77
+ },
78
+ )
79
+ ], className='three columns'),
80
+
81
+
82
+ ], className='row'),
83
+ html.Div(
84
+ [
85
+ html.Div(
86
+ [
87
+ html.H3('Policy'),
88
+ dcc.Graph(id='policy_readable-graph')
89
+ ], className='six columns',
90
+ ),
91
+ html.Div(
92
+ [
93
+ html.H3('Energy Consumption'),
94
+ dcc.Graph(id='energy-comsumption-graph')
95
+ ], className='six columns'
96
+ )
97
+ ], className='row'),
98
+ html.Div(
99
+ [
100
+ html.Div(
101
+ [
102
+ html.H3('Confort'),
103
+ dcc.Graph(id='confort-graph')
104
+ ], className='six columns'
105
+ )
106
+ ], className='row'),
107
+ ],
108
+ #add background image from local file and make it transparent
109
+ #, style={'background-image':'url(/assets/background_1.png)'}
110
+ style={'background-color': '#4B5320', 'font-family': 'Fantasy', 'color': '#8F9779', 'padding': '10px'}
111
+ )
112
+
113
+
114
+
115
+ wiki_layout = html.Div([
116
+ dcc.Link('Dashboard', href='/'),
117
+
118
+ html.H1('About this project'),
119
+
120
+ html.Div([
121
+ html.Div([
122
+
123
+ html.H3('What is this project about?'),
124
+
125
+ html.P('This project is a simulation of a shower system. The goal is to find the best policy for the boiler to heat the water for the shower. The policy is a function that takes the current hour of the day and the current temperature of the water in the boiler and returns the temperature that the boiler should heat the water to.'),
126
+ html.P('The best policy is the one that maximizes the confort of the shower and minimizes the energy consumption of the boiler.'),
127
+
128
+ html.H3('How does it work?'),
129
+
130
+ #Insert image of the system
131
+
132
+ html.H3('\'Bout us'),
133
+ html.Img(src='/assets/hackatos.png', style={'width': '40%', 'height': 'auto', 'display': 'block', 'margin-left': 'auto', 'margin-right': 'auto'}),
134
+ html.P('This project was developed by a team of 3, in the context of the Aveiro Tech City 2023 hackathon.'),
135
+ html.P('The team members are:'),
136
+ html.H4('Rui Melo'),
137
+ html.P('Rui Melo is a ....'),
138
+ html.H4('André Catarino'),
139
+ html.P('André Catarino is a ....'),
140
+ html.H4('Francisco Petronilho'),
141
+ html.P('Francisco Petronilho is a ....'),
142
+ html.H4('André Tomás'),
143
+ html.P('André Tomás is a ....'),
144
+ html.H4('Zé Miguel'),
145
+ html.P('Zé Miguel is a ....'),
146
+
147
+
148
+ html.H3('References'),
149
+ html.P('The boiler model was based on the following paper:'),
150
+
151
+
152
+ ], className='six columns'),], className='row'),
153
+ ],
154
+ style={'background-color': '#4B5320', 'font-family': 'Fantasy', 'color': '#8F9779', 'padding': '10px'}
155
+
156
+ )
157
+
158
+ # Update the index
159
+ @callback(Output('page-content', 'children'), Input('url', 'pathname'))
160
+ def display_page(pathname):
161
+ if pathname == '/':
162
+ return dashboard_layout
163
+ elif pathname == '/wiki':
164
+ return wiki_layout
165
+ else:
166
+ return '404'
167
+ # You could also return a 404 "URL not found" page here
168
+
169
+
170
+
171
+ @app.callback(
172
+ Output('policy_readable-graph', 'figure'),
173
+ Output('energy-comsumption-graph', 'figure'),
174
+ Output('confort-graph', 'figure'),
175
+ Output('solution-status', 'children'),
176
+ Input('solution-dropdown', 'value'),
177
+ Input('ideal-temperature-slider', 'value')
178
+ )
179
+ def update_graph(solution, ideal_temperature):
180
+ energy_consumption = data['energy_consumption'].values
181
+ confort_obtained = data['confort'].values
182
+
183
+
184
+ policy_readable_graph = px.line(data, x='hour', y='policy_readable',
185
+ labels={'hour': 'Hour', 'policy_readable': 'Policy'},
186
+ color_discrete_sequence=['lightgreen'])
187
+ policy_readable_graph.update_layout(
188
+ xaxis_title="Hour",
189
+ yaxis_title="Temperature (°C)",
190
+ legend_title="Policy"
191
+ )
192
+
193
+ energy_consumption_graph = px.line(data, x='hour',
194
+ y='energy_consumption',
195
+ labels={'hour': 'Hour', 'energy_consumption': 'Energy Consumption (kWh)'},
196
+ color_discrete_sequence=['lightgreen'])
197
+ energy_consumption_graph.update_layout(
198
+ xaxis_title="Hour",
199
+ yaxis_title="Energy Consumption (kWh)",
200
+ legend_title="Energy Consumption",
201
+ )
202
+ #add accumulated energy consumption
203
+ energy_consumption = np.cumsum(energy_consumption)
204
+ energy_consumption_graph.add_trace(px.line(data, x='hour',
205
+ y=energy_consumption,
206
+ labels={'y': 'Acc. Energy Consumption (kWh)'},
207
+ color_discrete_sequence=['green']).data[0])
208
+
209
+ confort_graph = px.line(data, x='hour', y='confort',
210
+ labels={'hour': 'Hour', 'confort': 'Confort Score'},
211
+ color_discrete_sequence=['lightgreen'])
212
+
213
+ confort_graph.update_layout(
214
+ xaxis_title="Hour",
215
+ yaxis_title="Confort Score",
216
+ legend_title="Confort"
217
+ )
218
+ #add accumulated confort
219
+ confort_obtained = np.cumsum(confort_obtained)
220
+ confort_graph.add_trace(px.line(data, x='hour', y=confort_obtained,
221
+ labels={'y': 'Acc. Confort Score'},
222
+ color_discrete_sequence=['green']
223
+ ).data[0])
224
+ result = "No solution found"
225
+ return policy_readable_graph, energy_consumption_graph, confort_graph, result
226
+ if __name__ == "__main__":
227
+ app.run_server(host="0.0.0.0", port="8050", debug=debug)
228
+
app/assets/.DS_Store ADDED
Binary file (6.15 kB). View file
 
app/assets/hackatos.png ADDED
app/core.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Electric cost(Euros) = energy_price(Euros/kWh) * wasted_energy(kWh)
2
+ def custo(energy_price_hour, wasted_energy):
3
+ return energy_price_hour * wasted_energy
4
+
5
+
6
+ # caldeira 20 litros
7
+ def spent_energy(
8
+ temperatura_inicial_caldeira_t, # existente na cadeira, t sendo a hora
9
+ temperatura_objetivo_caldeira_t_plus_1,
10
+ outside_temp,
11
+ pressao_caldeira,
12
+ litros_gastos_no_banho=0,
13
+ temperatura_entrada_agua_na_caldeira=15, # Temperatura ambiente da iNOVA
14
+ capacidade_caldeira=20, # 20 litros
15
+ ):
16
+ # E = (4.2 kJ/kgoC) ((90 oC) - (20 oC)) (1000 liter) (1 kg/liter)
17
+ # cp = specific heat of water (kJ/kgoC, Btu/lb oF) (4.2 kJ/kgoC, 1 Btu/lbmoF for water)
18
+ heat_capacity = 4.2
19
+ # Energy = heat_capacity * (temperatura_saida_agua_na_caldeira - outside_temp) * capacidade_caldeira * 1\
20
+ delta_t = temperatura_objetivo_caldeira_t_plus_1 - temperatura_inicial_caldeira_t
21
+
22
+ energy = (
23
+ heat_capacity
24
+ * (delta_t - outside_temp)
25
+ * (capacidade_caldeira - litros_gastos_no_banho)
26
+ * 1
27
+ ) # isto vai ser minimo
28
+
29
+ delta_t = (
30
+ temperatura_objetivo_caldeira_t_plus_1 - temperatura_entrada_agua_na_caldeira
31
+ )
32
+ energy_incoming_water = (
33
+ heat_capacity * (delta_t - outside_temp) * litros_gastos_no_banho * 1
34
+ )
35
+
36
+ # 20 Litros totais
37
+ # Joao gatou 5 litros
38
+ # Gastar energia em:
39
+ # 15 litros para manter a temperatura da caldeira -> minimo
40
+ # 5 litros para aquecer a agua que entra
41
+
42
+ total_energy = energy + energy_incoming_water
43
+
44
+ # TODO: Correlação entre pressão e temperatura
45
+ # https://www.engineeringtoolbox.com/boiling-points-water-altitude-d_1344.html
46
+ # https://www.engineeringtoolbox.com/boiling-point-water-d_926.html
47
+ """
48
+ That depends on whether the pressure is held constant during the heating. If there is a relief valve which maintains
49
+ constant pressure as the water heats, then no, the 2 samples will heat at the same rate. However, if the pressurised sample
50
+ has no pressure relief, then it will heat faster because the pressure will increase, and that increase in pressure will increase
51
+ the heat in addition to the heat applied.
52
+ """
53
+ # kJ
54
+
55
+ # TODO: FIND WHAT SHOULD BE THE RELATION BETWEEN TEMPERATURE OF OUTGOING WATER AND BOILER TEMPERATURE
56
+ temperatura_saida_agua_na_caldeira = temperatura_objetivo_caldeira_t_plus_1 * 0.87
57
+
58
+ return total_energy, temperatura_saida_agua_na_caldeira
59
+
60
+
61
+ def calculate_weights_for_all_hours(
62
+ temperatura_inicial_caldeira,
63
+ temperatura_objetivo_caldeira,
64
+ outside_temp,
65
+ pressao_caldeira,
66
+ litros_gastos_no_banho,
67
+ temperatura_entrada_agua_na_caldeira,
68
+ capacidade_caldeira,
69
+ ):
70
+ weights = []
71
+ temperatures = []
72
+ for i in range(len(temperatura_inicial_caldeira)):
73
+ energy, temperature_water = spent_energy(
74
+ temperatura_inicial_caldeira_t=temperatura_inicial_caldeira[i],
75
+ temperatura_objetivo_caldeira_t_plus_1=temperatura_objetivo_caldeira,
76
+ outside_temp=outside_temp[i],
77
+ pressao_caldeira=pressao_caldeira[i],
78
+ litros_gastos_no_banho=litros_gastos_no_banho,
79
+ temperatura_entrada_agua_na_caldeira=temperatura_entrada_agua_na_caldeira[
80
+ i
81
+ ],
82
+ capacidade_caldeira=capacidade_caldeira,
83
+ )
84
+ weights.append(energy)
85
+ temperatures.append(temperature_water)
86
+ return weights, temperatures
87
+ # Output energy wasted
88
+
89
+
90
+ def calculate_confort(temperatura_given, temperatura_ideal):
91
+ return abs(temperatura_given - temperatura_ideal)
92
+
93
+
94
+ # create exception for no solution found
95
+ class NoSolutionFound(Exception):
96
+ pass
97
+
98
+ class SoltionFoundWithLargerConfortValue(Exception):
99
+ pass
app/dashboard.ipynb ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 2,
6
+ "metadata": {},
7
+ "outputs": [
8
+ {
9
+ "name": "stderr",
10
+ "output_type": "stream",
11
+ "text": [
12
+ "/var/folders/b4/lwfgccm95kqd2skcwvrt2fr00000gn/T/ipykernel_7355/1062829466.py:19: FutureWarning:\n",
13
+ "\n",
14
+ "The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.\n",
15
+ "\n"
16
+ ]
17
+ },
18
+ {
19
+ "data": {
20
+ "text/html": [
21
+ "\n",
22
+ " <iframe\n",
23
+ " width=\"100%\"\n",
24
+ " height=\"650\"\n",
25
+ " src=\"http://0.0.0.0:8050/\"\n",
26
+ " frameborder=\"0\"\n",
27
+ " allowfullscreen\n",
28
+ " \n",
29
+ " ></iframe>\n",
30
+ " "
31
+ ],
32
+ "text/plain": [
33
+ "<IPython.lib.display.IFrame at 0x28c80a4d0>"
34
+ ]
35
+ },
36
+ "metadata": {},
37
+ "output_type": "display_data"
38
+ }
39
+ ],
40
+ "source": [
41
+ "import os\n",
42
+ "import plotly.express as px\n",
43
+ "import plotly.graph_objects as go\n",
44
+ "import pandas as pd\n",
45
+ "from dash import Dash, html, dcc, Input, Output, callback\n",
46
+ "import plotly.express as px\n",
47
+ "import numpy as np\n",
48
+ "import example_data\n",
49
+ "import core\n",
50
+ "\n",
51
+ "\n",
52
+ "outside_temp = example_data.ExampleDailyOutsideTemperature\n",
53
+ "energy_price = example_data.ExampleDailyEnergyCost\n",
54
+ "boiler_temperature = example_data.ExampleBoilerTemperature\n",
55
+ "\n",
56
+ "\n",
57
+ "\n",
58
+ "data = pd.DataFrame(columns=['hour', 'energy_consumption', 'confort', 'policy_readable'])\n",
59
+ "data = pd.concat([data, pd.DataFrame({'hour': np.arange(0, 24), 'energy_consumption': energy_price.value, 'confort': np.random.rand(24),\n",
60
+ " 'policy_readable': np.random.choice(['A', 'B', 'C', 'D', 'E'], 24)\n",
61
+ " })])\n",
62
+ "debug = False\n",
63
+ "\n",
64
+ "\n",
65
+ "external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']\n",
66
+ "\n",
67
+ "app = Dash(__name__, external_stylesheets=external_stylesheets)\n",
68
+ "\n",
69
+ "app.layout = html.Div([\n",
70
+ " dcc.Location(id='url', refresh=False),\n",
71
+ " html.Div(id='page-content')\n",
72
+ "])\n",
73
+ "\n",
74
+ "server = app.server\n",
75
+ "\n",
76
+ "\n",
77
+ "# SOlution options\n",
78
+ "solution_options = [\n",
79
+ " \"Discrete Optimization\",\n",
80
+ " \"Continuous Optimization\",\n",
81
+ " \"Reinforcement Learning\"\n",
82
+ "]\n",
83
+ "solution_options_default_value = solution_options[0]\n",
84
+ "\n",
85
+ "# provide a scalar value to enable the slider to select ideal temperature\n",
86
+ "ideal_temperature = 50\n",
87
+ "\n",
88
+ "dashboard_layout = html.Div([\n",
89
+ " dcc.Link('About this project', href='/wiki'),\n",
90
+ "\n",
91
+ " html.H1('System Evaluation'),\n",
92
+ " #small subtitle that says of solution is possible or not\n",
93
+ " html.Div(id='solution-status', children='', style={'color': 'lighrgrey'}),\n",
94
+ " html.Div([\n",
95
+ " html.Div([\n",
96
+ " html.H3('Solution'),\n",
97
+ " dcc.Dropdown(\n",
98
+ " id='solution-dropdown',\n",
99
+ " options=solution_options,\n",
100
+ " value=solution_options_default_value,\n",
101
+ " )\n",
102
+ " ], className='three columns'),\n",
103
+ " html.Div([\n",
104
+ " html.H3('Ideal Shower Temperature'),\n",
105
+ " dcc.Slider(\n",
106
+ " id='ideal-temperature-slider',\n",
107
+ " min=0,\n",
108
+ " max=100,\n",
109
+ " step=1,\n",
110
+ " value=ideal_temperature,\n",
111
+ " marks={\n",
112
+ " 0: '0°C',\n",
113
+ " 25: '25°C',\n",
114
+ " 50: '50°C',\n",
115
+ " 75: '75°C',\n",
116
+ " 100: '100°C'\n",
117
+ " },\n",
118
+ " )\n",
119
+ " ], className='three columns'),\n",
120
+ "\n",
121
+ "\n",
122
+ " ], className='row'),\n",
123
+ " html.Div(\n",
124
+ " [\n",
125
+ " html.Div(\n",
126
+ " [\n",
127
+ " html.H3('Policy'),\n",
128
+ " dcc.Graph(id='policy_readable-graph')\n",
129
+ " ], className='six columns',\n",
130
+ " ),\n",
131
+ " html.Div(\n",
132
+ " [\n",
133
+ " html.H3('Energy Consumption'),\n",
134
+ " dcc.Graph(id='energy-comsumption-graph')\n",
135
+ " ], className='six columns'\n",
136
+ " )\n",
137
+ " ], className='row'),\n",
138
+ " html.Div(\n",
139
+ " [\n",
140
+ " html.Div(\n",
141
+ " [\n",
142
+ " html.H3('Confort'),\n",
143
+ " dcc.Graph(id='confort-graph')\n",
144
+ " ], className='six columns'\n",
145
+ " )\n",
146
+ " ], className='row'),\n",
147
+ "],\n",
148
+ "#add background image from local file and make it transparent\n",
149
+ "#, style={'background-image':'url(/assets/background_1.png)'}\n",
150
+ "style={'background-color': '#4B5320', 'font-family': 'Fantasy', 'color': '#8F9779', 'padding': '10px'}\n",
151
+ ")\n",
152
+ "\n",
153
+ "\n",
154
+ "\n",
155
+ "wiki_layout = html.Div([\n",
156
+ " dcc.Link('Dashboard', href='/'),\n",
157
+ "\n",
158
+ " html.H1('About this project'),\n",
159
+ "\n",
160
+ " html.Div([\n",
161
+ " html.Div([\n",
162
+ " \n",
163
+ " html.H3('What is this project about?'),\n",
164
+ "\n",
165
+ " html.P('This project is a simulation of a shower system. The goal is to find the best policy for the boiler to heat the water for the shower. The policy is a function that takes the current hour of the day and the current temperature of the water in the boiler and returns the temperature that the boiler should heat the water to.'),\n",
166
+ " html.P('The best policy is the one that maximizes the confort of the shower and minimizes the energy consumption of the boiler.'),\n",
167
+ "\n",
168
+ " html.H3('How does it work?'),\n",
169
+ "\n",
170
+ " #Insert image of the system\n",
171
+ "\n",
172
+ " html.H3('\\'Bout us'),\n",
173
+ " html.Img(src='/assets/hackatos.png', style={'width': '40%', 'height': 'auto', 'display': 'block', 'margin-left': 'auto', 'margin-right': 'auto'}),\n",
174
+ " html.P('This project was developed by a team of 3, in the context of the Aveiro Tech City 2023 hackathon.'),\n",
175
+ " html.P('The team members are:'),\n",
176
+ " html.H4('Rui Melo'),\n",
177
+ " html.P('Rui Melo is a ....'),\n",
178
+ " html.H4('André Catarino'),\n",
179
+ " html.P('André Catarino is a ....'),\n",
180
+ " html.H4('Francisco Petronilho'),\n",
181
+ " html.P('Francisco Petronilho is a ....'),\n",
182
+ " html.H4('André Tomás'),\n",
183
+ " html.P('André Tomás is a ....'),\n",
184
+ " html.H4('Zé Miguel'),\n",
185
+ " html.P('Zé Miguel is a ....'),\n",
186
+ "\n",
187
+ "\n",
188
+ " html.H3('References'),\n",
189
+ " html.P('The boiler model was based on the following paper:'),\n",
190
+ "\n",
191
+ "\n",
192
+ " ], className='six columns'),], className='row'),\n",
193
+ "],\n",
194
+ "style={'background-color': '#4B5320', 'font-family': 'Fantasy', 'color': '#8F9779', 'padding': '10px'}\n",
195
+ "\n",
196
+ ")\n",
197
+ "\n",
198
+ "# Update the index\n",
199
+ "@callback(Output('page-content', 'children'), Input('url', 'pathname'))\n",
200
+ "def display_page(pathname):\n",
201
+ " if pathname == '/':\n",
202
+ " return dashboard_layout\n",
203
+ " elif pathname == '/wiki':\n",
204
+ " return wiki_layout\n",
205
+ " else:\n",
206
+ " return '404'\n",
207
+ " # You could also return a 404 \"URL not found\" page here\n",
208
+ "\n",
209
+ "\n",
210
+ "\n",
211
+ "@app.callback(\n",
212
+ " Output('policy_readable-graph', 'figure'),\n",
213
+ " Output('energy-comsumption-graph', 'figure'),\n",
214
+ " Output('confort-graph', 'figure'),\n",
215
+ " Output('solution-status', 'children'),\n",
216
+ " Input('solution-dropdown', 'value'),\n",
217
+ " Input('ideal-temperature-slider', 'value')\n",
218
+ ")\n",
219
+ "def update_graph(solution, ideal_temperature):\n",
220
+ " energy_consumption = data['energy_consumption'].values\n",
221
+ " confort_obtained = data['confort'].values\n",
222
+ "\n",
223
+ "\n",
224
+ " policy_readable_graph = px.line(data, x='hour', y='policy_readable',\n",
225
+ " labels={'hour': 'Hour', 'policy_readable': 'Policy'},\n",
226
+ " color_discrete_sequence=['lightgreen'])\n",
227
+ " policy_readable_graph.update_layout(\n",
228
+ " xaxis_title=\"Hour\",\n",
229
+ " yaxis_title=\"Temperature (°C)\",\n",
230
+ " legend_title=\"Policy\"\n",
231
+ " )\n",
232
+ "\n",
233
+ " energy_consumption_graph = px.line(data, x='hour', \n",
234
+ " y='energy_consumption', \n",
235
+ " labels={'hour': 'Hour', 'energy_consumption': 'Energy Consumption (kWh)'},\n",
236
+ " color_discrete_sequence=['lightgreen'])\n",
237
+ " energy_consumption_graph.update_layout(\n",
238
+ " xaxis_title=\"Hour\",\n",
239
+ " yaxis_title=\"Energy Consumption (kWh)\",\n",
240
+ " legend_title=\"Energy Consumption\",\n",
241
+ " )\n",
242
+ " #add accumulated energy consumption\n",
243
+ " energy_consumption = np.cumsum(energy_consumption)\n",
244
+ " energy_consumption_graph.add_trace(px.line(data, x='hour', \n",
245
+ " y=energy_consumption,\n",
246
+ " labels={'y': 'Acc. Energy Consumption (kWh)'},\n",
247
+ " color_discrete_sequence=['green']).data[0])\n",
248
+ "\n",
249
+ " confort_graph = px.line(data, x='hour', y='confort',\n",
250
+ " labels={'hour': 'Hour', 'confort': 'Confort Score'},\n",
251
+ " color_discrete_sequence=['lightgreen'])\n",
252
+ "\n",
253
+ " confort_graph.update_layout(\n",
254
+ " xaxis_title=\"Hour\",\n",
255
+ " yaxis_title=\"Confort Score\",\n",
256
+ " legend_title=\"Confort\"\n",
257
+ " )\n",
258
+ " #add accumulated confort\n",
259
+ " confort_obtained = np.cumsum(confort_obtained)\n",
260
+ " confort_graph.add_trace(px.line(data, x='hour', y=confort_obtained,\n",
261
+ " labels={'y': 'Acc. Confort Score'},\n",
262
+ " color_discrete_sequence=['green']\n",
263
+ " ).data[0])\n",
264
+ " result = \"No solution found\"\n",
265
+ " return policy_readable_graph, energy_consumption_graph, confort_graph, result\n",
266
+ "if __name__ == \"__main__\":\n",
267
+ " app.run_server(host=\"0.0.0.0\", port=\"8050\", debug=debug)\n",
268
+ "\n"
269
+ ]
270
+ }
271
+ ],
272
+ "metadata": {
273
+ "kernelspec": {
274
+ "display_name": "atc-smart-shower-YhjpRjjr-py3.10",
275
+ "language": "python",
276
+ "name": "python3"
277
+ },
278
+ "language_info": {
279
+ "codemirror_mode": {
280
+ "name": "ipython",
281
+ "version": 3
282
+ },
283
+ "file_extension": ".py",
284
+ "mimetype": "text/x-python",
285
+ "name": "python",
286
+ "nbconvert_exporter": "python",
287
+ "pygments_lexer": "ipython3",
288
+ "version": "3.10.13"
289
+ },
290
+ "orig_nbformat": 4
291
+ },
292
+ "nbformat": 4,
293
+ "nbformat_minor": 2
294
+ }
app/example_data.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+
3
+
4
+ @dataclass
5
+ class ExampleDailyOutsideTemperature:
6
+ """Outside temperature in degrees Celsius"""
7
+
8
+ value = [
9
+ 9.05,
10
+ 8.38,
11
+ 7.61,
12
+ 6.85,
13
+ 6.41,
14
+ 5.66,
15
+ 5.77,
16
+ 5.02,
17
+ 4.97,
18
+ 5.15,
19
+ 7.26,
20
+ 8.06,
21
+ 8.47,
22
+ 8.59,
23
+ 8.86,
24
+ 8.87,
25
+ 8.86,
26
+ 8.71,
27
+ 8.36,
28
+ 8.11,
29
+ 7.87,
30
+ 7.7,
31
+ 7.98,
32
+ 7.62,
33
+ ]
34
+ temperatura_entrada_agua = [x * 0.9 for x in value]
35
+
36
+ temperatura_saida_agua = 50
37
+
38
+ def temperatura_entrada_no_banho(self, potencia):
39
+ return [value * potencia for value in self.value]
40
+
41
+
42
+ @dataclass
43
+ class ExampleBoilerTemperature:
44
+ """Boiler temperature in degrees Celsius"""
45
+
46
+ value = [
47
+ 9.05,
48
+ 8.38,
49
+ 7.61,
50
+ 6.85,
51
+ 6.41,
52
+ 5.66,
53
+ 5.77,
54
+ 5.02,
55
+ 4.97,
56
+ 5.15,
57
+ 7.26,
58
+ 8.06,
59
+ 8.47,
60
+ 8.59,
61
+ 8.86,
62
+ 8.87,
63
+ 8.86,
64
+ 8.71,
65
+ 8.36,
66
+ 8.11,
67
+ 7.87,
68
+ 7.7,
69
+ 7.98,
70
+ 7.62,
71
+ ]
72
+
73
+ temperature = [x * 2 for x in value]
74
+
75
+
76
+ @dataclass
77
+ class ExampleDailyEnergyCost:
78
+ """Energy cost in eur per kWh"""
79
+
80
+ value = [
81
+ 0.5266399999999999,
82
+ 0.45166399999999995,
83
+ 0.34220000000000006,
84
+ 0.32719999999999994,
85
+ 0.3608,
86
+ 0.45879199999999987,
87
+ 0.4177600000000001,
88
+ 0.43716800000000006,
89
+ 0.4400000000000001,
90
+ 0.44004800000000005,
91
+ 0.440016,
92
+ 0.4400000000000001,
93
+ 0.4400000000000001,
94
+ 0.4412,
95
+ 0.4412,
96
+ 0.44480000000000003,
97
+ 0.44160000000000005,
98
+ 0.44160000000000005,
99
+ 0.45439999999999997,
100
+ 0.45439999999999997,
101
+ 0.4512,
102
+ 0.4400000000000001,
103
+ 0.428208,
104
+ 0.49184000000000005,
105
+ ]
106
+
107
+
108
+ @dataclass
109
+ class ExampleBoilerPressure:
110
+ value = [
111
+ 0.5266399999999999,
112
+ 0.45166399999999995,
113
+ 0.34220000000000006,
114
+ 0.32719999999999994,
115
+ 0.3608,
116
+ 0.45879199999999987,
117
+ 0.4177600000000001,
118
+ 0.43716800000000006,
119
+ 0.4400000000000001,
120
+ 0.44004800000000005,
121
+ 0.440016,
122
+ 0.4400000000000001,
123
+ 0.4400000000000001,
124
+ 0.4412,
125
+ 0.4412,
126
+ 0.44480000000000003,
127
+ 0.44160000000000005,
128
+ 0.44160000000000005,
129
+ 0.45439999999999997,
130
+ 0.45439999999999997,
131
+ 0.4512,
132
+ 0.4400000000000001,
133
+ 0.428208,
134
+ 0.49184000000000005,
135
+ ]
136
+
137
+ pressure = [x * 4.3 for x in value]
138
+
139
+
140
+ TEMPERATURA_IDEAL = 40
app/requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ dash
2
+ plotly
3
+ pandas
4
+ gunicorn
5
+ wandb==0.15.5
6
+ scipy
dashboard.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+
3
+ data = pd.DataFrame(columns=["Dataset", "Alpha", "Top K", "Recall", "Precision"])
4
+ data = pd.concat(
5
+ [
6
+ data,
7
+ pd.DataFrame(
8
+ [["ml-100k", 0.1, 20, 0.2, 0.2]],
9
+ columns=["Dataset", "Alpha", "Top K", "Recall", "Precision"],
10
+ ),
11
+ ]
12
+ )
13
+
14
+ import os
15
+ import plotly.express as px
16
+ import pandas as pd
17
+ from dash import Dash, html, dcc, Input, Output, callback
18
+ import plotly.express as px
19
+ from dataclasses import dataclass
20
+ import json
21
+
22
+ data = pd.DataFrame(columns=["Dataset", "Alpha", "Top K", "Recall", "Precision"])
23
+ data = pd.concat(
24
+ [
25
+ data,
26
+ pd.DataFrame(
27
+ [["ml-100k", 0.1, 20, 0.2, 0.2]],
28
+ columns=["Dataset", "Alpha", "Top K", "Recall", "Precision"],
29
+ ),
30
+ ]
31
+ )
32
+ debug = False
33
+
34
+
35
+ external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
36
+
37
+ app = Dash(__name__, external_stylesheets=external_stylesheets)
38
+
39
+ server = app.server
40
+
41
+
42
+ dataset_options = [
43
+ {"label": entry, "value": entry} for entry in data["Dataset"].unique()
44
+ ]
45
+ dataset_options_default_value = data["Dataset"].unique()[0]
46
+
47
+ alpha_options = [{"label": entry, "value": entry} for entry in data["Alpha"].unique()]
48
+ alpha_options_default_value = data["Alpha"].unique()[0]
49
+
50
+ top_k_options = [{"label": entry, "value": entry} for entry in data["Top K"].unique()]
51
+ top_k_options_default_value = data["Top K"].unique()[0]
52
+
53
+ app.layout = html.Div(
54
+ [
55
+ html.H1("System Evaluation"),
56
+ html.Div(
57
+ [
58
+ html.Div(
59
+ [
60
+ html.H3("Dataset"),
61
+ dcc.Dropdown(
62
+ id="dataset-dropdown",
63
+ options=dataset_options,
64
+ value=dataset_options_default_value,
65
+ ),
66
+ ],
67
+ className="three columns",
68
+ ),
69
+ html.Div(
70
+ [
71
+ html.H3("Alpha"),
72
+ dcc.Dropdown(
73
+ id="alpha-dropdown",
74
+ options=alpha_options,
75
+ value=alpha_options_default_value,
76
+ ),
77
+ ],
78
+ className="three columns",
79
+ ),
80
+ html.Div(
81
+ [
82
+ html.H3("Top K"),
83
+ dcc.Dropdown(
84
+ id="top_k-dropdown",
85
+ options=top_k_options,
86
+ value=top_k_options_default_value,
87
+ ),
88
+ ],
89
+ className="three columns",
90
+ ),
91
+ ],
92
+ className="row",
93
+ ),
94
+ html.Div(
95
+ [
96
+ html.Div([dcc.Graph(id="recall-graph")], className="six columns"),
97
+ html.Div([dcc.Graph(id="precision-graph")], className="six columns"),
98
+ ],
99
+ className="row",
100
+ ),
101
+ ]
102
+ )
103
+
104
+
105
+ @app.callback(
106
+ Output("recall-graph", "figure"),
107
+ Output("precision-graph", "figure"),
108
+ Input("alpha-dropdown", "value"),
109
+ Input("dataset-dropdown", "value"),
110
+ Input("top_k-dropdown", "value"),
111
+ )
112
+ def update_graph(alpha, dataset, top_k):
113
+ filtered_data = data[
114
+ (data["Alpha"] == alpha)
115
+ & (data["Dataset"] == dataset)
116
+ & (data["Top K"] == top_k)
117
+ ]
118
+ recall_fig = px.bar(filtered_data, x="Dataset", y="Recall")
119
+ precision_fig = px.bar(filtered_data, x="Dataset", y="Precision")
120
+ return recall_fig, precision_fig
121
+
122
+
123
+ # Run app and display result inline in the notebook
124
+ if __name__ == "__main__":
125
+ app.run_server(debug=debug, port=8050)