OpenSimula 0.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opensimula/Child.py +19 -0
- opensimula/Component.py +214 -0
- opensimula/Iterative_process.py +47 -0
- opensimula/Message.py +19 -0
- opensimula/Parameter_container.py +80 -0
- opensimula/Parameters.py +755 -0
- opensimula/Project.py +674 -0
- opensimula/Simulation.py +212 -0
- opensimula/Variable.py +37 -0
- opensimula/__init__.py +8 -0
- opensimula/components/Building.py +511 -0
- opensimula/components/Building_surface.py +318 -0
- opensimula/components/Calculator.py +51 -0
- opensimula/components/Construction.py +309 -0
- opensimula/components/Day_schedule.py +64 -0
- opensimula/components/File_data.py +117 -0
- opensimula/components/File_met.py +1035 -0
- opensimula/components/Frame.py +35 -0
- opensimula/components/Glazing.py +122 -0
- opensimula/components/HVAC_DX_equipment.py +210 -0
- opensimula/components/HVAC_DX_system.py +254 -0
- opensimula/components/HVAC_MZW_system.py +554 -0
- opensimula/components/HVAC_SZW_system.py +338 -0
- opensimula/components/HVAC_coil_equipment.py +206 -0
- opensimula/components/HVAC_fan_equipment.py +44 -0
- opensimula/components/HVAC_perfect_system.py +137 -0
- opensimula/components/Material.py +14 -0
- opensimula/components/Opening.py +409 -0
- opensimula/components/Opening_type.py +90 -0
- opensimula/components/Solar_surface.py +114 -0
- opensimula/components/Space.py +388 -0
- opensimula/components/Space_type.py +81 -0
- opensimula/components/Surface.py +129 -0
- opensimula/components/Test_component.py +76 -0
- opensimula/components/Week_schedule.py +30 -0
- opensimula/components/Year_schedule.py +65 -0
- opensimula/components/__init__.py +40 -0
- opensimula/visual_3D/Environment_3D.py +257 -0
- opensimula/visual_3D/Polygon_3D.py +262 -0
- opensimula-0.5.1.dist-info/METADATA +49 -0
- opensimula-0.5.1.dist-info/RECORD +44 -0
- opensimula-0.5.1.dist-info/WHEEL +5 -0
- opensimula-0.5.1.dist-info/licenses/LICENSE +21 -0
- opensimula-0.5.1.dist-info/top_level.txt +1 -0
opensimula/Project.py
ADDED
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import datetime as dt
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from dash import Dash, callback, Input, Output, html, State
|
|
6
|
+
import dash_ag_grid as dag
|
|
7
|
+
import dash_bootstrap_components as dbc
|
|
8
|
+
import psychrolib as sicro
|
|
9
|
+
from tqdm import trange
|
|
10
|
+
from opensimula.Message import Message
|
|
11
|
+
from opensimula.Parameter_container import Parameter_container
|
|
12
|
+
from opensimula.Parameters import (
|
|
13
|
+
Parameter_component,
|
|
14
|
+
Parameter_int,
|
|
15
|
+
Parameter_string,
|
|
16
|
+
Parameter_string_list,
|
|
17
|
+
Parameter_boolean,
|
|
18
|
+
Parameter_options,
|
|
19
|
+
Parameter_float
|
|
20
|
+
)
|
|
21
|
+
from opensimula.components import *
|
|
22
|
+
from opensimula.visual_3D.Environment_3D import Environment_3D
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Project(Parameter_container):
|
|
26
|
+
"""Project has the following features:
|
|
27
|
+
|
|
28
|
+
- It is included in the Simulation environment
|
|
29
|
+
- Contain a list of components
|
|
30
|
+
- Contains parameters for its definition
|
|
31
|
+
- Contains Environment_3D for 3D representation of the project
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, name, sim):
|
|
35
|
+
"""Create new project
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
sim (Simulation): parent Simulation environment
|
|
39
|
+
"""
|
|
40
|
+
Parameter_container.__init__(self, sim)
|
|
41
|
+
self.parameter("name").value = name
|
|
42
|
+
self.parameter("description").value = "Description of the project"
|
|
43
|
+
self.add_parameter(Parameter_int("time_step", 3600, "s", min=1))
|
|
44
|
+
self.add_parameter(Parameter_int("n_time_steps", 8760, min=1))
|
|
45
|
+
self.add_parameter(Parameter_string("initial_time", "01/01/2001 00:00:00"))
|
|
46
|
+
self.add_parameter(Parameter_boolean("daylight_saving", False))
|
|
47
|
+
self.add_parameter(
|
|
48
|
+
Parameter_string("daylight_saving_start_time", "25/03/2001 02:00:00")
|
|
49
|
+
)
|
|
50
|
+
self.add_parameter(
|
|
51
|
+
Parameter_string("daylight_saving_end_time", "28/10/2001 02:00:00")
|
|
52
|
+
)
|
|
53
|
+
self.add_parameter(Parameter_int("n_max_iteration", 1000, min=1))
|
|
54
|
+
self.add_parameter(
|
|
55
|
+
Parameter_string_list("simulation_order", DEFAULT_COMPONENTS_ORDER)
|
|
56
|
+
)
|
|
57
|
+
self.add_parameter(
|
|
58
|
+
Parameter_component("simulation_file_met", "not_defined", ["File_met"])
|
|
59
|
+
)
|
|
60
|
+
self.add_parameter(
|
|
61
|
+
Parameter_options(
|
|
62
|
+
"shadow_calculation", "INSTANT", ["NO", "INSTANT", "INTERPOLATION"]
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
self.add_parameter(Parameter_float("albedo", 0.2, "frac", min=0, max=1))
|
|
66
|
+
|
|
67
|
+
self._sim_ = sim
|
|
68
|
+
self._components_ = []
|
|
69
|
+
sicro.SetUnitSystem(sicro.SI)
|
|
70
|
+
atm_p = sicro.GetStandardAtmPressure(0)
|
|
71
|
+
w_50 = sicro.GetHumRatioFromRelHum(22.5, 0.5, atm_p)
|
|
72
|
+
|
|
73
|
+
def rhocp_water(T):
|
|
74
|
+
"""
|
|
75
|
+
Returns: Liquid water rho*c_p at 1 atm (J/(m^3·K)), Cubic adjustment
|
|
76
|
+
|
|
77
|
+
Parameters:
|
|
78
|
+
T: water temperature (ºC)
|
|
79
|
+
"""
|
|
80
|
+
return (
|
|
81
|
+
-6.5515e-08 * T**3 + 7.6219e-06 * T**2 - 1.8564e-03 * T + 4.2125
|
|
82
|
+
) * 1e6
|
|
83
|
+
|
|
84
|
+
# Set simulation properties
|
|
85
|
+
self._sim_.props = {
|
|
86
|
+
"C_PA": 1006, # J/kg·K
|
|
87
|
+
"C_P_FURNITURE": 1000, # J/kg·K
|
|
88
|
+
"LAMBDA": 2501, # J/g Latent heat of water at 0ºC
|
|
89
|
+
"ALTITUDE": 0, # m
|
|
90
|
+
"ATM_PRESSURE": atm_p,
|
|
91
|
+
"W_50": w_50,
|
|
92
|
+
"RHO_A": sicro.GetMoistAirDensity(22.5, w_50, atm_p),
|
|
93
|
+
"RHOCP_W": rhocp_water,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
def del_component(self, component):
|
|
97
|
+
"""Delete component from Project
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
component (Component): Component to be removed from the project
|
|
101
|
+
"""
|
|
102
|
+
self._components_.remove(component)
|
|
103
|
+
|
|
104
|
+
def component(self, name):
|
|
105
|
+
"""Find and return component with its name
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
name (string): name of the component
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
component (Component): component found, None if not found.
|
|
112
|
+
"""
|
|
113
|
+
for comp in self._components_:
|
|
114
|
+
if comp.parameter("name").value == name:
|
|
115
|
+
return comp
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
def component_list(self, comp_type="all"):
|
|
119
|
+
"""Components list in the project
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
components (Components list): List of components.
|
|
123
|
+
"""
|
|
124
|
+
comp_list = []
|
|
125
|
+
for comp in self._components_:
|
|
126
|
+
if comp_type == "all":
|
|
127
|
+
comp_list.append(comp)
|
|
128
|
+
else:
|
|
129
|
+
if comp.parameter("type").value == comp_type:
|
|
130
|
+
comp_list.append(comp)
|
|
131
|
+
return comp_list
|
|
132
|
+
|
|
133
|
+
def component_dataframe(self, comp_type="all", string_format=False):
|
|
134
|
+
data = pd.DataFrame()
|
|
135
|
+
comp_list = self.component_list(comp_type)
|
|
136
|
+
if len(comp_list) > 0:
|
|
137
|
+
parameters = ["name", "type", "description"]
|
|
138
|
+
if comp_type != "all":
|
|
139
|
+
for key, par in comp_list[0]._parameters_.items():
|
|
140
|
+
if key != "name" and key != "type" and key != "description":
|
|
141
|
+
parameters.append(key)
|
|
142
|
+
for param in parameters:
|
|
143
|
+
param_array = []
|
|
144
|
+
for comp in comp_list:
|
|
145
|
+
if string_format:
|
|
146
|
+
param_array.append(str(comp.parameter(param).value))
|
|
147
|
+
else:
|
|
148
|
+
param_array.append(comp.parameter(param).value)
|
|
149
|
+
data[param] = param_array
|
|
150
|
+
return data
|
|
151
|
+
|
|
152
|
+
def new_component(self, comp_type, name):
|
|
153
|
+
try:
|
|
154
|
+
clase = globals()[comp_type]
|
|
155
|
+
comp = clase(name, self)
|
|
156
|
+
self._components_.append(comp)
|
|
157
|
+
return comp
|
|
158
|
+
except KeyError:
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
def _get_error_header_(self):
|
|
162
|
+
return f'Project "{self.parameter("name").value}". '
|
|
163
|
+
|
|
164
|
+
def _load_from_dict_(self, dic):
|
|
165
|
+
for key, value in dic.items():
|
|
166
|
+
if key == "components": # Lista de componentes
|
|
167
|
+
for component in value:
|
|
168
|
+
if "type" in component:
|
|
169
|
+
name = component["type"] + "_X"
|
|
170
|
+
if "name" in component:
|
|
171
|
+
name = component["name"]
|
|
172
|
+
comp = self.new_component(component["type"], name)
|
|
173
|
+
if comp is None:
|
|
174
|
+
msg = (
|
|
175
|
+
self._get_error_header_()
|
|
176
|
+
+ f'Component type {component["type"]} does not exist.'
|
|
177
|
+
)
|
|
178
|
+
self._sim_.message(Message(msg, "ERROR"))
|
|
179
|
+
else:
|
|
180
|
+
comp.set_parameters(component)
|
|
181
|
+
else:
|
|
182
|
+
msg = (
|
|
183
|
+
self._get_error_header_()
|
|
184
|
+
+ f'Component does not contain "type" parameter {component}'
|
|
185
|
+
)
|
|
186
|
+
self._sim_.message(Message(msg, "ERROR"))
|
|
187
|
+
else:
|
|
188
|
+
if key in self._parameters_:
|
|
189
|
+
self.parameter(key).value = value
|
|
190
|
+
else:
|
|
191
|
+
msg = self._get_error_header_() + f"Parameter {key} does not exist."
|
|
192
|
+
self._sim_.message(Message(msg, "ERROR"))
|
|
193
|
+
|
|
194
|
+
def read_dict(self, dict):
|
|
195
|
+
"""Load paramaters an components from dictionary
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
dic (dictionary): dictonary with the parameters and componenets to be loaded in the project
|
|
199
|
+
|
|
200
|
+
"""
|
|
201
|
+
self._sim_.message(Message("Reading project data from dictonary", "CONSOLE"))
|
|
202
|
+
self._load_from_dict_(dict)
|
|
203
|
+
self._sim_.message(Message("Reading completed.", "CONSOLE"))
|
|
204
|
+
self.check()
|
|
205
|
+
|
|
206
|
+
def write_dict(self):
|
|
207
|
+
"""Write dictionary with the definition of the project
|
|
208
|
+
|
|
209
|
+
Return:
|
|
210
|
+
dic (dictionary): dictonary with the parameters and componenets that define the project
|
|
211
|
+
|
|
212
|
+
"""
|
|
213
|
+
dict = {"components": []}
|
|
214
|
+
for key, param in self.parameter_dict().items():
|
|
215
|
+
dict[key] = param.value
|
|
216
|
+
for comp in self._components_:
|
|
217
|
+
comp_dict = {}
|
|
218
|
+
for key, param in comp.parameter_dict().items():
|
|
219
|
+
comp_dict[key] = param.value
|
|
220
|
+
dict["components"].append(comp_dict)
|
|
221
|
+
|
|
222
|
+
return dict
|
|
223
|
+
|
|
224
|
+
def read_json(self, json_file):
|
|
225
|
+
"""Read paramaters an components from dictionary in a json file
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
json_file (string): file name that contains dictonary with the parameters and componenets to be loaded in the project
|
|
229
|
+
|
|
230
|
+
"""
|
|
231
|
+
try:
|
|
232
|
+
f = open(json_file, "r")
|
|
233
|
+
except OSError:
|
|
234
|
+
msg = self._get_error_header_() + f"Could not open/read file: {json_file}."
|
|
235
|
+
self._sim_.message(Message(msg, "ERROR"))
|
|
236
|
+
return False
|
|
237
|
+
with f:
|
|
238
|
+
json_dict = json.load(f)
|
|
239
|
+
self._sim_.message(
|
|
240
|
+
Message("Reading project data from file: " + json_file, "CONSOLE")
|
|
241
|
+
)
|
|
242
|
+
self._load_from_dict_(json_dict)
|
|
243
|
+
self._sim_.message(Message("Reading completed.", "CONSOLE"))
|
|
244
|
+
self.check()
|
|
245
|
+
|
|
246
|
+
def write_json(self, json_file):
|
|
247
|
+
"""Write project definition to json file
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
json_file (string): file name
|
|
251
|
+
|
|
252
|
+
"""
|
|
253
|
+
try:
|
|
254
|
+
f = open(json_file, "w")
|
|
255
|
+
except OSError:
|
|
256
|
+
msg = self._get_error_header_() + f"Could not write file: {json_file}."
|
|
257
|
+
self._sim_.message(Message(msg, "ERROR"))
|
|
258
|
+
return False
|
|
259
|
+
with f:
|
|
260
|
+
self._sim_.message(
|
|
261
|
+
Message("Writing project data to file: " + json_file, "CONSOLE")
|
|
262
|
+
)
|
|
263
|
+
dict = self.write_dict()
|
|
264
|
+
json.dump(dict, f)
|
|
265
|
+
self._sim_.message(Message("Writing completed.", "CONSOLE"))
|
|
266
|
+
|
|
267
|
+
def _read_excel_(self, excel_file):
|
|
268
|
+
"""Read paramaters an components from excel file
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
excel_file (string): excel file path
|
|
272
|
+
"""
|
|
273
|
+
try:
|
|
274
|
+
xls_file = pd.ExcelFile(excel_file)
|
|
275
|
+
self._sim_.message(
|
|
276
|
+
Message("Reading project data from file: " + excel_file, "CONSOLE")
|
|
277
|
+
)
|
|
278
|
+
json_dict = self._excel_to_json_(xls_file)
|
|
279
|
+
self._load_from_dict_(json_dict)
|
|
280
|
+
self._sim_.message(Message("Reading completed.", "CONSOLE"))
|
|
281
|
+
self.check()
|
|
282
|
+
except Exception as e:
|
|
283
|
+
msg = self._get_error_header_() + f"Reading file: {excel_file} -> {e}."
|
|
284
|
+
self._sim_.message(Message(msg, "ERROR"))
|
|
285
|
+
return False
|
|
286
|
+
|
|
287
|
+
def _excel_to_json_(self, xls_file):
|
|
288
|
+
json = {"components": []}
|
|
289
|
+
sheets = xls_file.sheet_names
|
|
290
|
+
# project sheet
|
|
291
|
+
project_df = xls_file.parse(sheet_name="project")
|
|
292
|
+
for index, row in project_df.iterrows():
|
|
293
|
+
json[row["key"]] = self._value_to_json_(row["value"])
|
|
294
|
+
# rest of sheets
|
|
295
|
+
for sheet in sheets:
|
|
296
|
+
if sheet != "project":
|
|
297
|
+
comp_df = xls_file.parse(sheet_name=sheet)
|
|
298
|
+
column_names = comp_df.columns.values.tolist()
|
|
299
|
+
for index, row in comp_df.iterrows():
|
|
300
|
+
j = 0
|
|
301
|
+
comp_json = {}
|
|
302
|
+
comp_json["type"] = sheet
|
|
303
|
+
for cell in row:
|
|
304
|
+
comp_json[column_names[j]] = self._value_to_json_(cell)
|
|
305
|
+
j += 1
|
|
306
|
+
json["components"].append(comp_json)
|
|
307
|
+
return json
|
|
308
|
+
|
|
309
|
+
def _value_to_json_(self, value):
|
|
310
|
+
if isinstance(value, str):
|
|
311
|
+
if value[0] == "[":
|
|
312
|
+
return value[1:-1].split(",")
|
|
313
|
+
else:
|
|
314
|
+
return value
|
|
315
|
+
else:
|
|
316
|
+
return value
|
|
317
|
+
|
|
318
|
+
# ____________________
|
|
319
|
+
|
|
320
|
+
def _set_ordered_component_list_(self):
|
|
321
|
+
all_comp_list = []
|
|
322
|
+
# Add referenced components
|
|
323
|
+
for comp in self.component_list():
|
|
324
|
+
components = comp.get_all_referenced_components()
|
|
325
|
+
for comp_i in components:
|
|
326
|
+
if comp_i not in all_comp_list:
|
|
327
|
+
all_comp_list.append(comp_i)
|
|
328
|
+
# order components
|
|
329
|
+
self._ordered_component_list_ = all_comp_list.copy()
|
|
330
|
+
# Remove components and at the end
|
|
331
|
+
for comp_type in self.parameter("simulation_order").value:
|
|
332
|
+
for comp in all_comp_list:
|
|
333
|
+
if comp.parameter("type").value == comp_type:
|
|
334
|
+
self._ordered_component_list_.remove(comp)
|
|
335
|
+
for comp in all_comp_list:
|
|
336
|
+
if comp.parameter("type").value == comp_type:
|
|
337
|
+
self._ordered_component_list_.append(comp)
|
|
338
|
+
|
|
339
|
+
def check(self):
|
|
340
|
+
"""Check if all is correct, for the project and all its components
|
|
341
|
+
|
|
342
|
+
Prints all errors found
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
errors (string list): List of errors
|
|
346
|
+
"""
|
|
347
|
+
self._sim_.message(
|
|
348
|
+
Message("Checking project: " + self.parameter("name").value, "CONSOLE")
|
|
349
|
+
)
|
|
350
|
+
errors = self.check_parameters() # Parameters
|
|
351
|
+
names = []
|
|
352
|
+
# Check initial time
|
|
353
|
+
try:
|
|
354
|
+
dt.datetime.strptime(
|
|
355
|
+
self.parameter("initial_time").value, "%d/%m/%Y %H:%M:%S"
|
|
356
|
+
)
|
|
357
|
+
except ValueError:
|
|
358
|
+
msg = (
|
|
359
|
+
self._get_error_header_()
|
|
360
|
+
+ f"Initial_time: {self.parameter('initial_time').value} does not match format (dd/mm/yyyy HH:MM:SS)"
|
|
361
|
+
)
|
|
362
|
+
errors.append(Message(msg, "ERROR"))
|
|
363
|
+
# Check daylight saving dates
|
|
364
|
+
if self.parameter("daylight_saving").value:
|
|
365
|
+
try:
|
|
366
|
+
dt.datetime.strptime(
|
|
367
|
+
self.parameter("daylight_saving_start_time").value,
|
|
368
|
+
"%d/%m/%Y %H:%M:%S",
|
|
369
|
+
)
|
|
370
|
+
except ValueError:
|
|
371
|
+
msg = (
|
|
372
|
+
self._get_error_header_()
|
|
373
|
+
+ f"Initial_time: {self.parameter('daylight_saving_start_time').value} does not match format (dd/mm/yyyy HH:MM:SS)"
|
|
374
|
+
)
|
|
375
|
+
errors.append(Message(msg, "ERROR"))
|
|
376
|
+
try:
|
|
377
|
+
dt.datetime.strptime(
|
|
378
|
+
self.parameter("daylight_saving_end_time").value,
|
|
379
|
+
"%d/%m/%Y %H:%M:%S",
|
|
380
|
+
)
|
|
381
|
+
except ValueError:
|
|
382
|
+
error = (
|
|
383
|
+
self._get_error_header_()
|
|
384
|
+
+ f"Initial_time: {self.parameter('daylight_saving_end_time').value} does not match format (dd/mm/yyyy HH:MM:SS)"
|
|
385
|
+
)
|
|
386
|
+
errors.append(Message(msg, "ERROR"))
|
|
387
|
+
|
|
388
|
+
self._set_ordered_component_list_()
|
|
389
|
+
list = self._ordered_component_list_
|
|
390
|
+
for comp in list:
|
|
391
|
+
error_comp = comp.check()
|
|
392
|
+
if len(error_comp) > 0:
|
|
393
|
+
for e in error_comp:
|
|
394
|
+
errors.append(e)
|
|
395
|
+
if comp.parameter("name").value in names:
|
|
396
|
+
msg = (
|
|
397
|
+
self._get_error_header_()
|
|
398
|
+
+ f"'{comp.parameter('name').value}' is used by two or more components as name"
|
|
399
|
+
)
|
|
400
|
+
errors.append(Message(msg, "ERROR"))
|
|
401
|
+
else:
|
|
402
|
+
names.append(comp.parameter("name").value)
|
|
403
|
+
|
|
404
|
+
if len(errors) == 0:
|
|
405
|
+
self._sim_.message(Message("Checking completed.", "CONSOLE"))
|
|
406
|
+
else:
|
|
407
|
+
for error in errors:
|
|
408
|
+
self._sim_.message(error)
|
|
409
|
+
|
|
410
|
+
return errors
|
|
411
|
+
|
|
412
|
+
def simulate(self):
|
|
413
|
+
"""Project Time Simulation"""
|
|
414
|
+
n = self.parameter("n_time_steps").value
|
|
415
|
+
date = dt.datetime.strptime(
|
|
416
|
+
self.parameter("initial_time").value, "%d/%m/%Y %H:%M:%S"
|
|
417
|
+
)
|
|
418
|
+
delta_t = self.parameter("time_step").value
|
|
419
|
+
date = date + dt.timedelta(0, delta_t / 2) # Centered in the interval
|
|
420
|
+
if self.parameter("daylight_saving").value:
|
|
421
|
+
date_dls_start = dt.datetime.strptime(
|
|
422
|
+
self.parameter("daylight_saving_start_time").value, "%d/%m/%Y %H:%M:%S"
|
|
423
|
+
)
|
|
424
|
+
date_dls_end = dt.datetime.strptime(
|
|
425
|
+
self.parameter("daylight_saving_end_time").value, "%d/%m/%Y %H:%M:%S"
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
# Update props
|
|
429
|
+
if self.parameter("simulation_file_met").value != "not_defined":
|
|
430
|
+
met_file = self.component(self.parameter("simulation_file_met").value)
|
|
431
|
+
else:
|
|
432
|
+
met_file = None
|
|
433
|
+
try:
|
|
434
|
+
if met_file is not None:
|
|
435
|
+
altitude = met_file.altitude
|
|
436
|
+
atm_p = sicro.GetStandardAtmPressure(altitude)
|
|
437
|
+
w_50 = sicro.GetHumRatioFromRelHum(22.5, 0.5, atm_p)
|
|
438
|
+
self._sim_.props["ATM_PRESSURE"] = atm_p
|
|
439
|
+
self._sim_.props["ALTITUDE"] = altitude
|
|
440
|
+
self._sim_.props["W_50"] = w_50
|
|
441
|
+
self._sim_.props["RHO_A"] = sicro.GetMoistAirDensity(22.5, w_50, atm_p)
|
|
442
|
+
except Exception as e:
|
|
443
|
+
msg = (self._get_error_header_()
|
|
444
|
+
+ f"Error reading file: {self.parameter('simulation_file_met').value} -> {e}")
|
|
445
|
+
self._sim_.message(Message(msg, "ERROR"))
|
|
446
|
+
|
|
447
|
+
self._set_ordered_component_list_()
|
|
448
|
+
|
|
449
|
+
# Initialize 3D environment
|
|
450
|
+
self.env_3D = Environment_3D()
|
|
451
|
+
if self.parameter("shadow_calculation").value != "NO":
|
|
452
|
+
self.create_3D_environment(self.env_3D)
|
|
453
|
+
self._sim_.message(Message("Calculating solar direct shadows ...", "CONSOLE"))
|
|
454
|
+
self.env_3D.calculate_solar_tables()
|
|
455
|
+
self._pre_simulation_(n, delta_t)
|
|
456
|
+
self._sim_df = pd.DataFrame({"dates": self.dates()})
|
|
457
|
+
self._sim_df["n_iterations"] = 0
|
|
458
|
+
self._sim_df["last_component"] = "None"
|
|
459
|
+
self._sim_df["converged"] = True
|
|
460
|
+
|
|
461
|
+
self._sim_.message(
|
|
462
|
+
Message(f"Simulating {self.parameter('name').value}: ...", "CONSOLE")
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
tq = trange(n, unit="step", colour="blue")
|
|
466
|
+
for i in tq:
|
|
467
|
+
daylight_saving = False
|
|
468
|
+
if self.parameter("daylight_saving").value:
|
|
469
|
+
if date > date_dls_start and date < date_dls_end:
|
|
470
|
+
daylight_saving = True
|
|
471
|
+
|
|
472
|
+
# Update Shadows
|
|
473
|
+
if self.parameter("shadow_calculation").value == "INSTANT" and met_file is not None:
|
|
474
|
+
cos = met_file.sun_cosines(date)
|
|
475
|
+
if len(cos) == 3:
|
|
476
|
+
self.env_3D.calculate_shadows(cos,create_polygons=False)
|
|
477
|
+
else:
|
|
478
|
+
self.env_3D.delete_shadows()
|
|
479
|
+
|
|
480
|
+
self._pre_iteration_(i, date, daylight_saving)
|
|
481
|
+
converge = False
|
|
482
|
+
n_iter = 0
|
|
483
|
+
while not converge and n_iter < self.parameter("n_max_iteration").value:
|
|
484
|
+
if self._iteration_(i, date, daylight_saving, n_iter):
|
|
485
|
+
converge = True
|
|
486
|
+
n_iter += 1
|
|
487
|
+
tq.set_postfix(n_iter=n_iter)
|
|
488
|
+
self._sim_df.at[i, "n_iterations"] = n_iter
|
|
489
|
+
self._sim_df.at[i, "converged"] = converge
|
|
490
|
+
self._post_iteration_(i, date, daylight_saving, converge)
|
|
491
|
+
date = date + dt.timedelta(0, delta_t)
|
|
492
|
+
|
|
493
|
+
n_not_converged = (
|
|
494
|
+
len(self._sim_df["converged"]) - self._sim_df["converged"].sum()
|
|
495
|
+
)
|
|
496
|
+
if n_not_converged > 0:
|
|
497
|
+
self._sim_.message(
|
|
498
|
+
Message(f"{n_not_converged} time steps did not converge.", "WARNING")
|
|
499
|
+
)
|
|
500
|
+
self._post_simulation_()
|
|
501
|
+
|
|
502
|
+
def _pre_simulation_(self, n_time_steps, delta_t):
|
|
503
|
+
for comp in self._ordered_component_list_:
|
|
504
|
+
comp.pre_simulation(n_time_steps, delta_t)
|
|
505
|
+
|
|
506
|
+
def _post_simulation_(self):
|
|
507
|
+
for comp in self._ordered_component_list_:
|
|
508
|
+
comp.post_simulation()
|
|
509
|
+
|
|
510
|
+
def _pre_iteration_(self, time_index, date, dayligth_saving):
|
|
511
|
+
for comp in self._ordered_component_list_:
|
|
512
|
+
comp.pre_iteration(time_index, date, dayligth_saving)
|
|
513
|
+
|
|
514
|
+
def _iteration_(self, time_index, date, dayligth_saving, n_iter):
|
|
515
|
+
converge = True
|
|
516
|
+
for comp in self._ordered_component_list_:
|
|
517
|
+
if not comp.iteration(time_index, date, dayligth_saving, n_iter):
|
|
518
|
+
self._sim_df.at[time_index, "last_component"] = comp.parameter(
|
|
519
|
+
"name"
|
|
520
|
+
).value
|
|
521
|
+
converge = False
|
|
522
|
+
return converge
|
|
523
|
+
|
|
524
|
+
def _post_iteration_(self, time_index, date, dayligth_saving, converged):
|
|
525
|
+
for comp in self._ordered_component_list_:
|
|
526
|
+
comp.post_iteration(time_index, date, dayligth_saving, converged)
|
|
527
|
+
|
|
528
|
+
def dates(self):
|
|
529
|
+
n = self.parameter("n_time_steps").value
|
|
530
|
+
date = dt.datetime.strptime(
|
|
531
|
+
self.parameter("initial_time").value, "%d/%m/%Y %H:%M:%S"
|
|
532
|
+
)
|
|
533
|
+
delta_t = self.parameter("time_step").value
|
|
534
|
+
date = date + +dt.timedelta(0, delta_t / 2) # Centered in the interval
|
|
535
|
+
array = np.empty(n, dtype=object)
|
|
536
|
+
|
|
537
|
+
for i in range(n):
|
|
538
|
+
array[i] = date
|
|
539
|
+
date = date + dt.timedelta(0, delta_t)
|
|
540
|
+
|
|
541
|
+
return array
|
|
542
|
+
|
|
543
|
+
def _repr_html_(self):
|
|
544
|
+
html = f"<h3>Project: {self.parameter('name').value}</h3><p>{self.parameter('description').value}</p>"
|
|
545
|
+
html += "<strong>Parameters:</strong>"
|
|
546
|
+
html += self.parameter_dataframe().to_html()
|
|
547
|
+
html += "<br/><strong>Components list:</strong>"
|
|
548
|
+
html += self.component_dataframe().to_html()
|
|
549
|
+
return html
|
|
550
|
+
|
|
551
|
+
def simulation_dataframe(self):
|
|
552
|
+
return self._sim_df
|
|
553
|
+
|
|
554
|
+
def component_editor(self, comp_type="all"):
|
|
555
|
+
editor = Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
|
|
556
|
+
df = self.component_dataframe(comp_type=comp_type, string_format=True)
|
|
557
|
+
self._n_clicks_new_comp_ = 0
|
|
558
|
+
self._n_clicks_del_comp_ = 0
|
|
559
|
+
disabled_new = False
|
|
560
|
+
if comp_type == "all":
|
|
561
|
+
disabled_new = True
|
|
562
|
+
|
|
563
|
+
column_definition = [
|
|
564
|
+
{
|
|
565
|
+
"field": "name",
|
|
566
|
+
"checkboxSelection": True,
|
|
567
|
+
"headerCheckboxSelection": True,
|
|
568
|
+
}
|
|
569
|
+
]
|
|
570
|
+
for i in df.columns:
|
|
571
|
+
if i != "name":
|
|
572
|
+
column_definition.append({"field": i})
|
|
573
|
+
|
|
574
|
+
editor.layout = html.Div(
|
|
575
|
+
[
|
|
576
|
+
dbc.Label("Components editor:"),
|
|
577
|
+
html.Br(),
|
|
578
|
+
dbc.Button(
|
|
579
|
+
"New component",
|
|
580
|
+
id="btn-new-comp",
|
|
581
|
+
disabled=disabled_new,
|
|
582
|
+
n_clicks=0,
|
|
583
|
+
),
|
|
584
|
+
dbc.Button(
|
|
585
|
+
"Delete selected components",
|
|
586
|
+
id="btn-del-comp",
|
|
587
|
+
n_clicks=0,
|
|
588
|
+
style={"margin-left": "15px"},
|
|
589
|
+
),
|
|
590
|
+
html.Br(),
|
|
591
|
+
html.Br(),
|
|
592
|
+
dag.AgGrid(
|
|
593
|
+
id="comp-table",
|
|
594
|
+
rowData=df.to_dict("records"),
|
|
595
|
+
columnDefs=column_definition,
|
|
596
|
+
columnSize="sizeToFit",
|
|
597
|
+
defaultColDef={"filter": True, "editable": True},
|
|
598
|
+
style={"height": "500px"},
|
|
599
|
+
dashGridOptions={
|
|
600
|
+
"rowSelection": "multiple",
|
|
601
|
+
"suppressRowClickSelection": True,
|
|
602
|
+
"pagination": True,
|
|
603
|
+
},
|
|
604
|
+
),
|
|
605
|
+
]
|
|
606
|
+
)
|
|
607
|
+
|
|
608
|
+
@callback(
|
|
609
|
+
Output("comp-table", "rowData"),
|
|
610
|
+
Input("comp-table", "cellValueChanged"),
|
|
611
|
+
Input("btn-new-comp", "n_clicks"),
|
|
612
|
+
Input("btn-del-comp", "n_clicks"),
|
|
613
|
+
State("comp-table", "selectedRows"),
|
|
614
|
+
prevent_initial_call=True,
|
|
615
|
+
)
|
|
616
|
+
def update_data(changed, n_clicks_new, n_clicks_del, selectedRows):
|
|
617
|
+
if self._n_clicks_new_comp_ < n_clicks_new:
|
|
618
|
+
self.new_component(comp_type, "new_comp_" + str(n_clicks_new))
|
|
619
|
+
self._n_clicks_new_comp_ = n_clicks_new
|
|
620
|
+
elif self._n_clicks_del_comp_ < n_clicks_del:
|
|
621
|
+
for row in selectedRows:
|
|
622
|
+
self.del_component(self.component(row["name"]))
|
|
623
|
+
self._n_clicks_del_comp_ = n_clicks_del
|
|
624
|
+
else:
|
|
625
|
+
if changed is not None:
|
|
626
|
+
if changed[0]["colId"] == "name":
|
|
627
|
+
self.component(changed[0]["oldValue"]).parameter(
|
|
628
|
+
"name"
|
|
629
|
+
).value = changed[0]["value"]
|
|
630
|
+
else:
|
|
631
|
+
self.component(changed[0]["data"]["name"]).parameter(
|
|
632
|
+
changed[0]["colId"]
|
|
633
|
+
).value = changed[0]["value"]
|
|
634
|
+
df_end = self.component_dataframe(comp_type=comp_type, string_format=True)
|
|
635
|
+
return df_end.to_dict("records")
|
|
636
|
+
|
|
637
|
+
editor.run(jupyter_height=600)
|
|
638
|
+
|
|
639
|
+
def create_3D_environment(self, env_3D):
|
|
640
|
+
for component in self.component_list("all"):
|
|
641
|
+
if hasattr(component, "get_polygon_3D"):
|
|
642
|
+
env_3D.add_polygon_3D(component.get_polygon_3D())
|
|
643
|
+
|
|
644
|
+
def show_3D(self):
|
|
645
|
+
env_3D = Environment_3D()
|
|
646
|
+
self.create_3D_environment(env_3D)
|
|
647
|
+
env_3D.show(polygons_type="initial")
|
|
648
|
+
|
|
649
|
+
def show_3D_shadows(self, date):
|
|
650
|
+
env_3D = Environment_3D()
|
|
651
|
+
self.create_3D_environment(env_3D)
|
|
652
|
+
file_met = self.parameter("simulation_file_met").component
|
|
653
|
+
cos = file_met.sun_cosines(date)
|
|
654
|
+
if len(cos) == 3:
|
|
655
|
+
env_3D.calculate_shadows(cos)
|
|
656
|
+
env_3D.show(polygons_type="Building_shadows")
|
|
657
|
+
else:
|
|
658
|
+
self._sim_.message(Message(date.strftime("%H:%M, %d/%m/%Y") + " is night", "WARNING"))
|
|
659
|
+
|
|
660
|
+
def show_3D_shadows_animation(self, date):
|
|
661
|
+
env_3D = Environment_3D()
|
|
662
|
+
self.create_3D_environment(env_3D)
|
|
663
|
+
file_met = self.parameter("simulation_file_met").component
|
|
664
|
+
texts = []
|
|
665
|
+
cosines = []
|
|
666
|
+
for hour in range(24):
|
|
667
|
+
new_date = date.replace(hour=hour, minute=0, second=0)
|
|
668
|
+
cos = file_met.sun_cosines(new_date)
|
|
669
|
+
if len(cos) == 3: # Day time
|
|
670
|
+
texts.append(new_date.strftime("%H:%M, %d/%m/%Y"))
|
|
671
|
+
cosines.append(cos)
|
|
672
|
+
env_3D.show_animation(texts, cosines, polygons_type="Building_shadows")
|
|
673
|
+
|
|
674
|
+
|