OpenSimula 0.5.2__tar.gz → 0.6.1__tar.gz
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-0.5.2 → opensimula-0.6.1}/PKG-INFO +6 -7
- {opensimula-0.5.2 → opensimula-0.6.1}/README.md +5 -6
- {opensimula-0.5.2 → opensimula-0.6.1}/pyproject.toml +2 -1
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/Project.py +35 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/Simulation.py +10 -5
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/Variable.py +4 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/__init__.py +1 -1
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Building.py +1 -1
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Building_surface.py +226 -51
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Construction.py +14 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/File_met.py +98 -15
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/HVAC_MZW_system.py +283 -139
- opensimula-0.6.1/src/opensimula/components/HVAC_SZW_system.py +498 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/HVAC_perfect_system.py +7 -2
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Material.py +7 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Space.py +5 -2
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Surface.py +12 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula.egg-info/PKG-INFO +6 -7
- {opensimula-0.5.2 → opensimula-0.6.1}/test/test_Calculator.py +2 -2
- {opensimula-0.5.2 → opensimula-0.6.1}/test/test_Case_600FF.py +2 -2
- {opensimula-0.5.2 → opensimula-0.6.1}/test/test_Case_610.py +2 -2
- {opensimula-0.5.2 → opensimula-0.6.1}/test/test_Construction.py +4 -4
- {opensimula-0.5.2 → opensimula-0.6.1}/test/test_File_data.py +5 -5
- {opensimula-0.5.2 → opensimula-0.6.1}/test/test_File_met.py +39 -5
- {opensimula-0.5.2 → opensimula-0.6.1}/test/test_HVAC_DX_Equipment.py +2 -2
- {opensimula-0.5.2 → opensimula-0.6.1}/test/test_HVAC_DX_system.py +3 -3
- {opensimula-0.5.2 → opensimula-0.6.1}/test/test_Iterative_process.py +2 -2
- {opensimula-0.5.2 → opensimula-0.6.1}/test/test_Project.py +37 -7
- {opensimula-0.5.2 → opensimula-0.6.1}/test/test_Schedules.py +4 -4
- opensimula-0.5.2/src/opensimula/components/HVAC_SZW_system.py +0 -338
- {opensimula-0.5.2 → opensimula-0.6.1}/LICENSE +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/setup.cfg +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/Child.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/Component.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/Iterative_process.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/Message.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/Parameter_container.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/Parameters.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Calculator.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Day_schedule.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/File_data.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Frame.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Glazing.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/HVAC_DX_equipment.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/HVAC_DX_system.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/HVAC_coil_equipment.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/HVAC_fan_equipment.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Opening.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Opening_type.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Solar_surface.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Space_type.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Test_component.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Week_schedule.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/Year_schedule.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/components/__init__.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/visual_3D/Environment_3D.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula/visual_3D/Polygon_3D.py +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula.egg-info/SOURCES.txt +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula.egg-info/dependency_links.txt +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula.egg-info/requires.txt +0 -0
- {opensimula-0.5.2 → opensimula-0.6.1}/src/opensimula.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opensimula
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Component-based time simulation environment in Python
|
|
5
5
|
Author-email: "Juan F. Coronel" <jfc@us.es>
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -41,9 +41,8 @@ Component-based time simulation environment in Python
|
|
|
41
41
|
- dash
|
|
42
42
|
- dash_bootstrap_components
|
|
43
43
|
- dash_ag_grid
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
|
|
44
|
+
- nbformat
|
|
45
|
+
- openpyxl
|
|
46
|
+
- tqdm
|
|
47
|
+
- triangle
|
|
48
|
+
- vedo
|
|
@@ -17,9 +17,8 @@ Component-based time simulation environment in Python
|
|
|
17
17
|
- dash
|
|
18
18
|
- dash_bootstrap_components
|
|
19
19
|
- dash_ag_grid
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
|
|
20
|
+
- nbformat
|
|
21
|
+
- openpyxl
|
|
22
|
+
- tqdm
|
|
23
|
+
- triangle
|
|
24
|
+
- vedo
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "opensimula"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.1"
|
|
4
4
|
requires-python = ">=3.12"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name="Juan F. Coronel", email="jfc@us.es" },
|
|
@@ -29,6 +29,7 @@ dev = [
|
|
|
29
29
|
"ipykernel>=6.30.1",
|
|
30
30
|
"mkdocs>=1.6.1",
|
|
31
31
|
"pytest>=8.4.2",
|
|
32
|
+
"marimo>=0.19.0",
|
|
32
33
|
]
|
|
33
34
|
|
|
34
35
|
[build-system]
|
|
@@ -158,6 +158,41 @@ class Project(Parameter_container):
|
|
|
158
158
|
except KeyError:
|
|
159
159
|
return None
|
|
160
160
|
|
|
161
|
+
def duplicate_component(self, component_name, new_name):
|
|
162
|
+
"""Duplicate an existing component with a new name
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
component_name (string): name of the component to duplicate
|
|
166
|
+
new_name (string): name for the new component
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Component: the new duplicated component, None if error
|
|
170
|
+
"""
|
|
171
|
+
original = self.component(component_name)
|
|
172
|
+
if original is None:
|
|
173
|
+
msg = self._get_error_header_() + f"Component '{component_name}' not found."
|
|
174
|
+
self._sim_.message(Message(msg, "ERROR"))
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
# Get parameter dictionary from original component
|
|
178
|
+
comp_dict = {}
|
|
179
|
+
for key, param in original.parameter_dict().items():
|
|
180
|
+
comp_dict[key] = param.value
|
|
181
|
+
|
|
182
|
+
# Create new component of the same type
|
|
183
|
+
new_comp = self.new_component(comp_dict["type"], new_name)
|
|
184
|
+
if new_comp is None:
|
|
185
|
+
msg = self._get_error_header_() + f"Could not create component of type '{comp_dict['type']}'."
|
|
186
|
+
self._sim_.message(Message(msg, "ERROR"))
|
|
187
|
+
return None
|
|
188
|
+
|
|
189
|
+
# Copy parameters (except name and type)
|
|
190
|
+
for key, value in comp_dict.items():
|
|
191
|
+
if key != "name" and key != "type":
|
|
192
|
+
new_comp.parameter(key).value = value
|
|
193
|
+
|
|
194
|
+
return new_comp
|
|
195
|
+
|
|
161
196
|
def _get_error_header_(self):
|
|
162
197
|
return f'Project "{self.parameter("name").value}". '
|
|
163
198
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from opensimula.Project import Project
|
|
2
|
-
from opensimula.Message import Message
|
|
3
2
|
import pandas as pd
|
|
4
3
|
import plotly.express as px
|
|
5
4
|
from plotly.subplots import make_subplots
|
|
@@ -115,8 +114,11 @@ class Simulation:
|
|
|
115
114
|
if i < len(names):
|
|
116
115
|
series[names[i]] = variables[i].values
|
|
117
116
|
else:
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
if variables[i].parent is not None:
|
|
118
|
+
series[variables[i].parent.parameter(
|
|
119
|
+
"name").value+":"+variables[i].key] = variables[i].values
|
|
120
|
+
else:
|
|
121
|
+
series[variables[i].key] = variables[i].values
|
|
120
122
|
data = pd.DataFrame(series)
|
|
121
123
|
if frequency is not None:
|
|
122
124
|
if value == "mean":
|
|
@@ -135,8 +137,11 @@ class Simulation:
|
|
|
135
137
|
if i < len(names):
|
|
136
138
|
name = names[i]
|
|
137
139
|
else:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
+
if variables[i].parent is not None:
|
|
141
|
+
name = variables[i].parent.parameter(
|
|
142
|
+
"name").value+":"+variables[i].key
|
|
143
|
+
else:
|
|
144
|
+
name = variables[i].key
|
|
140
145
|
fig = px.line(data, x='date', y=name)
|
|
141
146
|
fig.for_each_trace(lambda t: t.update(name=name))
|
|
142
147
|
fig.update_traces(showlegend=True)
|
|
@@ -168,7 +168,7 @@ class Building(Component):
|
|
|
168
168
|
if self.spaces[j] == self.surfaces[i].get_space():
|
|
169
169
|
self.KSZ_matrix[i][j] = (
|
|
170
170
|
self.surfaces[i].area
|
|
171
|
-
* self.surfaces[i].parameter("h_cv").value[
|
|
171
|
+
* self.surfaces[i].parameter("h_cv").value[self.sides[i]]
|
|
172
172
|
)
|
|
173
173
|
elif surface_type == "INTERIOR":
|
|
174
174
|
k = self.surfaces[i].k
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import math
|
|
2
2
|
from opensimula.Message import Message
|
|
3
3
|
from opensimula.components.Surface import Surface
|
|
4
|
-
from opensimula.Parameters import
|
|
4
|
+
from opensimula.Parameters import (
|
|
5
|
+
Parameter_component,
|
|
6
|
+
Parameter_float,
|
|
7
|
+
Parameter_float_list,
|
|
8
|
+
Parameter_options,
|
|
9
|
+
Parameter_component_list,
|
|
10
|
+
)
|
|
5
11
|
from opensimula.Variable import Variable
|
|
6
12
|
from opensimula.visual_3D.Polygon_3D import Polygon_3D
|
|
7
13
|
|
|
@@ -12,10 +18,35 @@ class Building_surface(Surface):
|
|
|
12
18
|
# Parameters
|
|
13
19
|
self.parameter("type").value = "Building_surface"
|
|
14
20
|
self.parameter("description").value = "Building surface"
|
|
15
|
-
self.add_parameter(
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
self.add_parameter(
|
|
22
|
+
Parameter_component("construction", "not_defined", ["Construction"])
|
|
23
|
+
)
|
|
24
|
+
self.add_parameter(
|
|
25
|
+
Parameter_component_list(
|
|
26
|
+
"spaces", ["not_defined", "not_defined"], ["Space"]
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
self.add_parameter(
|
|
30
|
+
Parameter_options(
|
|
31
|
+
"surface_type",
|
|
32
|
+
"EXTERIOR",
|
|
33
|
+
["EXTERIOR", "INTERIOR", "UNDERGROUND", "VIRTUAL"],
|
|
34
|
+
)
|
|
35
|
+
)
|
|
18
36
|
self.add_parameter(Parameter_float_list("h_cv", [19.3, 2], "W/m²K", min=0))
|
|
37
|
+
# Only for underground surfaces
|
|
38
|
+
self.add_parameter(
|
|
39
|
+
Parameter_component("ground_material", "not_defined", ["Material"])
|
|
40
|
+
)
|
|
41
|
+
self.add_parameter(
|
|
42
|
+
Parameter_float("exterior_perimeter_fraction", 1, "frac", min=0, max=1)
|
|
43
|
+
)
|
|
44
|
+
self.add_parameter(
|
|
45
|
+
Parameter_float("exterior_perimeter_wall_thickness", 0.3, "m", min=0)
|
|
46
|
+
)
|
|
47
|
+
self.add_parameter(
|
|
48
|
+
Parameter_float("groundwater_depth", 0, "m", min=0)
|
|
49
|
+
)
|
|
19
50
|
|
|
20
51
|
# Constants
|
|
21
52
|
self.H_RD = 5.705 # 4*sigma*(293^3)
|
|
@@ -46,12 +77,12 @@ class Building_surface(Surface):
|
|
|
46
77
|
def check(self):
|
|
47
78
|
errors = super().check()
|
|
48
79
|
# Test space defined
|
|
49
|
-
|
|
50
|
-
if
|
|
80
|
+
self._surface_type_ = self.parameter("surface_type").value
|
|
81
|
+
if self._surface_type_ == "EXTERIOR" or self._surface_type_ == "UNDERGROUND":
|
|
51
82
|
if self.parameter("spaces").value[0] == "not_defined":
|
|
52
83
|
msg = f"{self.parameter('name').value}, must define its space."
|
|
53
84
|
errors.append(Message(msg, "ERROR"))
|
|
54
|
-
elif
|
|
85
|
+
elif self._surface_type_ == "INTERIOR" or self._surface_type_ == "VIRTUAL":
|
|
55
86
|
if (
|
|
56
87
|
self.parameter("spaces").value[0] == "not_defined"
|
|
57
88
|
or self.parameter("spaces").value[1] == "not_defined"
|
|
@@ -59,10 +90,20 @@ class Building_surface(Surface):
|
|
|
59
90
|
msg = f"{self.parameter('name').value}, must define two spaces."
|
|
60
91
|
errors.append(Message(msg, "ERROR"))
|
|
61
92
|
# Test construction defined for non-virtual surfaces
|
|
62
|
-
if
|
|
93
|
+
if (
|
|
94
|
+
self._surface_type_ != "VIRTUAL"
|
|
95
|
+
and self.parameter("construction").value == "not_defined"
|
|
96
|
+
):
|
|
63
97
|
msg = f"{self.parameter('name').value}, Building surfaces must define its construction."
|
|
64
98
|
errors.append(Message(msg, "ERROR"))
|
|
65
|
-
|
|
99
|
+
# Test ground material defined for underground surfaces
|
|
100
|
+
if (
|
|
101
|
+
self._surface_type_ == "UNDERGROUND"
|
|
102
|
+
and self.parameter("ground_material").value == "not_defined"
|
|
103
|
+
):
|
|
104
|
+
msg = f"{self.parameter('name').value}, Underground surfaces must define its ground material."
|
|
105
|
+
errors.append(Message(msg, "ERROR"))
|
|
106
|
+
# Create openings list
|
|
66
107
|
if self._surface_type_ == "EXTERIOR" or self._surface_type_ == "INTERIOR":
|
|
67
108
|
self._create_openings_list_()
|
|
68
109
|
return errors
|
|
@@ -70,17 +111,19 @@ class Building_surface(Surface):
|
|
|
70
111
|
def get_building(self):
|
|
71
112
|
return self.parameter("spaces").component[0].get_building()
|
|
72
113
|
|
|
73
|
-
def get_space(self,side=0):
|
|
114
|
+
def get_space(self, side=0):
|
|
74
115
|
return self.parameter("spaces").component[side]
|
|
75
|
-
|
|
116
|
+
|
|
76
117
|
def radiant_property(self, prop, radiation_type, side, theta=0):
|
|
77
|
-
if self.
|
|
118
|
+
if self._surface_type_ == "VIRTUAL":
|
|
78
119
|
if prop == "tau":
|
|
79
120
|
return 1
|
|
80
121
|
else:
|
|
81
122
|
return 0
|
|
82
123
|
else:
|
|
83
|
-
return self.parameter("construction").component.radiant_property(
|
|
124
|
+
return self.parameter("construction").component.radiant_property(
|
|
125
|
+
prop, radiation_type, side, theta
|
|
126
|
+
)
|
|
84
127
|
|
|
85
128
|
# ____________ pre_simulation ____________
|
|
86
129
|
def pre_simulation(self, n_time_steps, delta_t):
|
|
@@ -90,12 +133,102 @@ class Building_surface(Surface):
|
|
|
90
133
|
self._T_ini = self.get_building().parameter("initial_temperature").value
|
|
91
134
|
self._F_sky = (1 + math.sin(math.radians(self.parameter("altitude").value))) / 2
|
|
92
135
|
self._surface_type_ = self.parameter("surface_type").value
|
|
136
|
+
self._construction_ = self.parameter("construction").component
|
|
93
137
|
if self._surface_type_ == "EXTERIOR" or self._surface_type_ == "INTERIOR":
|
|
94
138
|
self._create_openings_list_()
|
|
95
|
-
if self._surface_type_ != "VIRTUAL":
|
|
96
139
|
self._calculate_K_()
|
|
97
140
|
if self._surface_type_ == "EXTERIOR":
|
|
98
|
-
self._sunny_index_ = self.project().env_3D.get_sunny_index(
|
|
141
|
+
self._sunny_index_ = self.project().env_3D.get_sunny_index(
|
|
142
|
+
self.parameter("name").value
|
|
143
|
+
)
|
|
144
|
+
if self._surface_type_ == "UNDERGROUND":
|
|
145
|
+
self._create_underground_construction_()
|
|
146
|
+
self._construction_.pre_simulation(
|
|
147
|
+
n_time_steps, delta_t
|
|
148
|
+
) # is not included in list of project components
|
|
149
|
+
self._calculate_K_()
|
|
150
|
+
self._calculate_ground_U_hat_()
|
|
151
|
+
|
|
152
|
+
def _create_underground_construction_(self):
|
|
153
|
+
# Test if underground construction already exists and delete it
|
|
154
|
+
und_cons_name = self.parameter("name").value + "_und_cons"
|
|
155
|
+
if self.project().component(und_cons_name) is not None:
|
|
156
|
+
self.project().del_component(self.project().component(und_cons_name))
|
|
157
|
+
# Test if virtual material already exists and delete it
|
|
158
|
+
vir_mat_name = self.parameter("name").value + "_vir_mat"
|
|
159
|
+
if self.project().component(vir_mat_name) is not None:
|
|
160
|
+
self.project().del_component(self.project().component(vir_mat_name))
|
|
161
|
+
# create virtual material with zero properties
|
|
162
|
+
vir_mat = self.project().new_component("Material", vir_mat_name)
|
|
163
|
+
vir_mat.set_parameters({"density": 1, "specific_heat": 1, "conductivity": 1})
|
|
164
|
+
# create new construction for underground surfaces
|
|
165
|
+
self._construction_ = self.project().duplicate_component(
|
|
166
|
+
self.parameter("construction").component.parameter("name").value,
|
|
167
|
+
und_cons_name,
|
|
168
|
+
)
|
|
169
|
+
# Equivalent thickness UNE-EN ISO 13370:2017
|
|
170
|
+
self._B_prime_ = (
|
|
171
|
+
2
|
|
172
|
+
* self.area
|
|
173
|
+
/ (self.perimeter * self.parameter("exterior_perimeter_fraction").value)
|
|
174
|
+
)
|
|
175
|
+
R_cons = self._construction_.thermal_resistance() # m²K/W
|
|
176
|
+
self._k_g_ = (
|
|
177
|
+
self.parameter("ground_material").component.parameter("conductivity").value
|
|
178
|
+
)
|
|
179
|
+
w = self.parameter("exterior_perimeter_wall_thickness").value # Thickness of perimetral walls
|
|
180
|
+
self._d_t_ = w + self._k_g_ * (
|
|
181
|
+
1 / self.parameter("h_cv").value[0]
|
|
182
|
+
+ 1 / self.parameter("h_cv").value[1]
|
|
183
|
+
+ R_cons
|
|
184
|
+
) # m
|
|
185
|
+
|
|
186
|
+
if self._d_t_ < self._B_prime_:
|
|
187
|
+
self._U_tot_ = (
|
|
188
|
+
(2 * self._k_g_)
|
|
189
|
+
/ (math.pi * self._B_prime_ + self._d_t_)
|
|
190
|
+
* math.log((math.pi * self._B_prime_ / self._d_t_) + 1)
|
|
191
|
+
)
|
|
192
|
+
else: # Well insulated
|
|
193
|
+
self._U_tot_ = self._k_g_ / (self._d_t_ + 0.457 * self._B_prime_)
|
|
194
|
+
|
|
195
|
+
# Gorundwater effect
|
|
196
|
+
groundwater_depth = self.parameter("groundwater_depth").value
|
|
197
|
+
self._G_w_ = 1.0
|
|
198
|
+
if groundwater_depth > 0:
|
|
199
|
+
self._G_w_ = 1 + math.exp(-2*groundwater_depth / self._B_prime_)
|
|
200
|
+
self._U_tot_ *= self._G_w_
|
|
201
|
+
|
|
202
|
+
# adding 0,5m of ground material t
|
|
203
|
+
self._ground_thickness_ = 0.5 # fixed 50 cm.
|
|
204
|
+
self._construction_.add_exterior_layer(
|
|
205
|
+
self.parameter("ground_material").component.parameter("name").value,
|
|
206
|
+
self._ground_thickness_,
|
|
207
|
+
)
|
|
208
|
+
# Thermal resistanace of the virtual layer
|
|
209
|
+
R_vi = (
|
|
210
|
+
1 / self._U_tot_
|
|
211
|
+
- 1 / self.parameter("h_cv").value[1]
|
|
212
|
+
- R_cons
|
|
213
|
+
- 0.5 / self._k_g_
|
|
214
|
+
) # m²K/W
|
|
215
|
+
k_vi = 0.1 / R_vi
|
|
216
|
+
# Add virtual layer to the construction
|
|
217
|
+
vir_mat.parameter("conductivity").value = k_vi
|
|
218
|
+
self._construction_.add_exterior_layer(vir_mat_name, 0.1)
|
|
219
|
+
|
|
220
|
+
def _calculate_ground_U_hat_(self):
|
|
221
|
+
# Exterior temperature from meteorological file
|
|
222
|
+
# Dinamics transmitance (U_hat and U_hat_ext)
|
|
223
|
+
periodo_segundos = 31536000 # Un año
|
|
224
|
+
rho_g = self.parameter("ground_material").component.parameter("density").value
|
|
225
|
+
c_p_g = (
|
|
226
|
+
self.parameter("ground_material").component.parameter("specific_heat").value
|
|
227
|
+
)
|
|
228
|
+
d_p = math.sqrt((self._k_g_ * periodo_segundos) / (math.pi * rho_g * c_p_g))
|
|
229
|
+
self._U_hat_ext_ = self._G_w_ * self._k_g_ / math.sqrt(
|
|
230
|
+
(0.457 * self._B_prime_ + self._d_t_) ** 2 + d_p**2
|
|
231
|
+
)
|
|
99
232
|
|
|
100
233
|
def _create_openings_list_(self):
|
|
101
234
|
project_openings_list = self.project().component_list(comp_type="Opening")
|
|
@@ -105,46 +238,55 @@ class Building_surface(Surface):
|
|
|
105
238
|
self.openings.append(opening)
|
|
106
239
|
|
|
107
240
|
def _calculate_K_(self):
|
|
108
|
-
self.a_0, self.a_1, self.a_01 = self.
|
|
241
|
+
self.a_0, self.a_1, self.a_01 = self._construction_.get_A()
|
|
109
242
|
self.k_01 = self.area * self.a_01
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
243
|
+
if self._surface_type_ == "EXTERIOR":
|
|
244
|
+
self.k[0] = self.area * (
|
|
245
|
+
self.a_0
|
|
113
246
|
- self.parameter("h_cv").value[0]
|
|
114
247
|
- self.H_RD * self.radiant_property("alpha", "long_wave", 0)
|
|
115
248
|
)
|
|
116
249
|
self.k[1] = self.area * (self.a_1 - self.parameter("h_cv").value[1])
|
|
117
|
-
elif
|
|
118
|
-
# interior
|
|
250
|
+
elif self._surface_type_ == "INTERIOR":
|
|
119
251
|
self.k[0] = self.area * (self.a_0 - self.parameter("h_cv").value[0])
|
|
120
252
|
self.k[1] = self.area * (self.a_1 - self.parameter("h_cv").value[1])
|
|
121
|
-
elif
|
|
122
|
-
# Underground
|
|
253
|
+
elif self._surface_type_ == "UNDERGROUND":
|
|
123
254
|
self.k[0] = 1 # not used
|
|
124
|
-
self.k[1] = self.area * (
|
|
125
|
-
|
|
255
|
+
self.k[1] = self.area * (
|
|
256
|
+
self.a_1 - self.parameter("h_cv").value[1]
|
|
257
|
+
) # only one defined
|
|
126
258
|
|
|
127
|
-
#________ pre_iteration ____________
|
|
259
|
+
# ________ pre_iteration ____________
|
|
128
260
|
def pre_iteration(self, time_i, date, daylight_saving):
|
|
129
261
|
super().pre_iteration(time_i, date, daylight_saving)
|
|
130
262
|
# Meterological data
|
|
131
263
|
self._T_ext = self._file_met.variable("temperature").values[time_i]
|
|
132
264
|
if self._surface_type_ != "VIRTUAL":
|
|
133
265
|
# Trasient part
|
|
134
|
-
self._p_0_, self._p_1_ = self.
|
|
266
|
+
self._p_0_, self._p_1_ = self._construction_.get_P(
|
|
135
267
|
time_i,
|
|
136
268
|
self.variable("T_s0").values,
|
|
137
269
|
self.variable("T_s1").values,
|
|
138
270
|
self.variable("q_cd0").values,
|
|
139
271
|
self.variable("q_cd1").values,
|
|
140
|
-
self._T_ini
|
|
272
|
+
self._T_ini,
|
|
273
|
+
)
|
|
141
274
|
self.variable("p_0").values[time_i] = self._p_0_
|
|
142
275
|
self.variable("p_1").values[time_i] = self._p_1_
|
|
143
276
|
if self._surface_type_ == "EXTERIOR":
|
|
144
277
|
self._pre_iteration_exterior_(time_i)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
278
|
+
if self._surface_type_ == "UNDERGROUND":
|
|
279
|
+
year_start = date.replace(month=1, day=1, hour=0, minute=0, second=0)
|
|
280
|
+
seconds_elapsed = (date - year_start).total_seconds()
|
|
281
|
+
DT = self._file_met.DT_monthly_amplitude/2
|
|
282
|
+
T_avg = self._file_met.T_average
|
|
283
|
+
tau = self._file_met.month_min_temp # One month of delay for the minimum temperature, as in UNE-EN ISO 13370:2017
|
|
284
|
+
m = seconds_elapsed / 2628000
|
|
285
|
+
self.variable("T_s0").values[time_i] = (
|
|
286
|
+
T_avg
|
|
287
|
+
- self._U_hat_ext_ / self._U_tot_ * DT * math.cos(2 * math.pi * (m-tau) / 12)
|
|
288
|
+
)
|
|
289
|
+
|
|
148
290
|
def _pre_iteration_exterior_(self, time_i):
|
|
149
291
|
# Meterological data
|
|
150
292
|
hor_sol_dif = self._file_met.variable("sol_diffuse").values[time_i]
|
|
@@ -153,33 +295,40 @@ class Building_surface(Surface):
|
|
|
153
295
|
|
|
154
296
|
# Diffuse solar radiation
|
|
155
297
|
E_dif_sunny = self._file_met.solar_diffuse_rad(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
E_dif_sunny = E_dif_sunny + (1 - self._F_sky) * self._albedo * (
|
|
298
|
+
time_i,
|
|
299
|
+
self.orientation_angle("azimuth", 0),
|
|
300
|
+
self.orientation_angle("altitude", 0),
|
|
301
|
+
)
|
|
302
|
+
E_dif_sunny = E_dif_sunny + (1 - self._F_sky) * self._albedo * (
|
|
303
|
+
hor_sol_dif + hor_sol_dir
|
|
304
|
+
)
|
|
161
305
|
self.variable("E_dif_sunny").values[time_i] = E_dif_sunny
|
|
162
|
-
diffuse_sunny_fraction = self.project().env_3D.get_diffuse_sunny_fraction(
|
|
306
|
+
diffuse_sunny_fraction = self.project().env_3D.get_diffuse_sunny_fraction(
|
|
307
|
+
self._sunny_index_
|
|
308
|
+
)
|
|
163
309
|
E_dif = E_dif_sunny * diffuse_sunny_fraction
|
|
164
310
|
self.variable("E_dif").values[time_i] = E_dif
|
|
165
311
|
# Direct solar radiation
|
|
166
312
|
E_dir_sunny = self._file_met.solar_direct_rad(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
313
|
+
time_i,
|
|
314
|
+
self.orientation_angle("azimuth", 0),
|
|
315
|
+
self.orientation_angle("altitude", 0),
|
|
170
316
|
)
|
|
171
317
|
self.variable("E_dir_sunny").values[time_i] = E_dir_sunny
|
|
172
318
|
E_dir = E_dir_sunny * self._calculate_direct_sunny_fraction_(time_i)
|
|
173
319
|
self.variable("E_dir").values[time_i] = E_dir
|
|
174
320
|
q_sol0 = self.radiant_property("alpha", "solar_diffuse", 0) * (E_dif + E_dir)
|
|
175
321
|
self.variable("q_sol0").values[time_i] = q_sol0
|
|
176
|
-
|
|
322
|
+
# Mean radiant temperature
|
|
177
323
|
T_rm = self._F_sky * T_sky + (1 - self._F_sky) * self._T_ext
|
|
178
324
|
self.variable("T_rm").values[time_i] = T_rm
|
|
179
325
|
# f_0
|
|
180
326
|
h_rd = self.H_RD * self.radiant_property("alpha", "long_wave", 0)
|
|
181
327
|
self.f_0 = self.area * (
|
|
182
|
-
-self._p_0_
|
|
328
|
+
-self._p_0_
|
|
329
|
+
- self.parameter("h_cv").value[0] * self._T_ext
|
|
330
|
+
- h_rd * T_rm
|
|
331
|
+
- q_sol0
|
|
183
332
|
)
|
|
184
333
|
|
|
185
334
|
def _calculate_direct_sunny_fraction_(self, time_i):
|
|
@@ -202,8 +351,7 @@ class Building_surface(Surface):
|
|
|
202
351
|
direct_sunny_fraction = 1
|
|
203
352
|
return direct_sunny_fraction
|
|
204
353
|
|
|
205
|
-
|
|
206
|
-
#________ post_iteration ____________
|
|
354
|
+
# ________ post_iteration ____________
|
|
207
355
|
def post_iteration(self, time_index, date, daylight_saving, converged):
|
|
208
356
|
super().post_iteration(time_index, date, daylight_saving, converged)
|
|
209
357
|
if self._surface_type_ == "EXTERIOR":
|
|
@@ -236,7 +384,8 @@ class Building_surface(Surface):
|
|
|
236
384
|
)
|
|
237
385
|
h_rd = self.H_RD * self.radiant_property("alpha", "long_wave", 0)
|
|
238
386
|
self.variable("q_lwt0").values[time_i] = h_rd * (
|
|
239
|
-
self.variable("T_rm").values[time_i]
|
|
387
|
+
self.variable("T_rm").values[time_i]
|
|
388
|
+
- self.variable("T_s0").values[time_i]
|
|
240
389
|
)
|
|
241
390
|
self.variable("q_lwt1").values[time_i] = (
|
|
242
391
|
-self.variable("q_cd1").values[time_i]
|
|
@@ -269,8 +418,10 @@ class Building_surface(Surface):
|
|
|
269
418
|
- self.variable("q_lwig1").values[time_i]
|
|
270
419
|
)
|
|
271
420
|
elif self._surface_type_ == "UNDERGROUND":
|
|
272
|
-
self.variable("q_cv0").values[time_i] = -self.variable("q_cd0").values[
|
|
273
|
-
|
|
421
|
+
self.variable("q_cv0").values[time_i] = -self.variable("q_cd0").values[
|
|
422
|
+
time_i
|
|
423
|
+
]
|
|
424
|
+
self.variable("q_cv1").values[time_i] = self.parameter("h_cv").value[1] * (
|
|
274
425
|
self.get_space().variable("temperature").values[time_i]
|
|
275
426
|
- self.variable("T_s1").values[time_i]
|
|
276
427
|
)
|
|
@@ -306,13 +457,37 @@ class Building_surface(Surface):
|
|
|
306
457
|
)
|
|
307
458
|
elif self._surface_type_ == "INTERIOR":
|
|
308
459
|
return Polygon_3D(
|
|
309
|
-
name,
|
|
460
|
+
name,
|
|
461
|
+
origin,
|
|
462
|
+
azimuth,
|
|
463
|
+
altitude,
|
|
464
|
+
pol_2D,
|
|
465
|
+
holes_2D,
|
|
466
|
+
color="green",
|
|
467
|
+
shading=False,
|
|
468
|
+
calculate_shadows=False,
|
|
310
469
|
)
|
|
311
|
-
elif self._surface_type_ == "UNDERGROUND":
|
|
470
|
+
elif self._surface_type_ == "UNDERGROUND":
|
|
312
471
|
return Polygon_3D(
|
|
313
|
-
name,
|
|
472
|
+
name,
|
|
473
|
+
origin,
|
|
474
|
+
azimuth,
|
|
475
|
+
altitude,
|
|
476
|
+
pol_2D,
|
|
477
|
+
holes_2D,
|
|
478
|
+
color="brown",
|
|
479
|
+
shading=False,
|
|
480
|
+
calculate_shadows=False,
|
|
314
481
|
)
|
|
315
482
|
elif self._surface_type_ == "VIRTUAL":
|
|
316
483
|
return Polygon_3D(
|
|
317
|
-
name,
|
|
484
|
+
name,
|
|
485
|
+
origin,
|
|
486
|
+
azimuth,
|
|
487
|
+
altitude,
|
|
488
|
+
pol_2D,
|
|
489
|
+
color="green",
|
|
490
|
+
opacity=0.4,
|
|
491
|
+
shading=False,
|
|
492
|
+
calculate_shadows=False,
|
|
318
493
|
)
|
|
@@ -28,6 +28,20 @@ class Construction(Component):
|
|
|
28
28
|
errors.append(Message(msg, "error"))
|
|
29
29
|
return errors
|
|
30
30
|
|
|
31
|
+
def add_exterior_layer(self, material_name, thickness):
|
|
32
|
+
"""Add a new exterior layer (at index 0) to the construction.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
material_name (str): Name of the material component to add
|
|
36
|
+
thickness (float): Thickness of the layer in meters
|
|
37
|
+
"""
|
|
38
|
+
materials = self.parameter("materials").value
|
|
39
|
+
thicknesses = self.parameter("thicknesses").value
|
|
40
|
+
materials.insert(0, material_name)
|
|
41
|
+
thicknesses.insert(0, thickness)
|
|
42
|
+
self.parameter("materials").value = materials
|
|
43
|
+
self.parameter("thicknesses").value = thicknesses
|
|
44
|
+
|
|
31
45
|
# Functions for Transfer Function Calculation
|
|
32
46
|
|
|
33
47
|
def pre_simulation(self, n_time_steps, delta_t):
|