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.
Files changed (44) hide show
  1. opensimula/Child.py +19 -0
  2. opensimula/Component.py +214 -0
  3. opensimula/Iterative_process.py +47 -0
  4. opensimula/Message.py +19 -0
  5. opensimula/Parameter_container.py +80 -0
  6. opensimula/Parameters.py +755 -0
  7. opensimula/Project.py +674 -0
  8. opensimula/Simulation.py +212 -0
  9. opensimula/Variable.py +37 -0
  10. opensimula/__init__.py +8 -0
  11. opensimula/components/Building.py +511 -0
  12. opensimula/components/Building_surface.py +318 -0
  13. opensimula/components/Calculator.py +51 -0
  14. opensimula/components/Construction.py +309 -0
  15. opensimula/components/Day_schedule.py +64 -0
  16. opensimula/components/File_data.py +117 -0
  17. opensimula/components/File_met.py +1035 -0
  18. opensimula/components/Frame.py +35 -0
  19. opensimula/components/Glazing.py +122 -0
  20. opensimula/components/HVAC_DX_equipment.py +210 -0
  21. opensimula/components/HVAC_DX_system.py +254 -0
  22. opensimula/components/HVAC_MZW_system.py +554 -0
  23. opensimula/components/HVAC_SZW_system.py +338 -0
  24. opensimula/components/HVAC_coil_equipment.py +206 -0
  25. opensimula/components/HVAC_fan_equipment.py +44 -0
  26. opensimula/components/HVAC_perfect_system.py +137 -0
  27. opensimula/components/Material.py +14 -0
  28. opensimula/components/Opening.py +409 -0
  29. opensimula/components/Opening_type.py +90 -0
  30. opensimula/components/Solar_surface.py +114 -0
  31. opensimula/components/Space.py +388 -0
  32. opensimula/components/Space_type.py +81 -0
  33. opensimula/components/Surface.py +129 -0
  34. opensimula/components/Test_component.py +76 -0
  35. opensimula/components/Week_schedule.py +30 -0
  36. opensimula/components/Year_schedule.py +65 -0
  37. opensimula/components/__init__.py +40 -0
  38. opensimula/visual_3D/Environment_3D.py +257 -0
  39. opensimula/visual_3D/Polygon_3D.py +262 -0
  40. opensimula-0.5.1.dist-info/METADATA +49 -0
  41. opensimula-0.5.1.dist-info/RECORD +44 -0
  42. opensimula-0.5.1.dist-info/WHEEL +5 -0
  43. opensimula-0.5.1.dist-info/licenses/LICENSE +21 -0
  44. opensimula-0.5.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,129 @@
1
+ import math
2
+ import numpy as np
3
+ from opensimula.Component import Component
4
+ from opensimula.Message import Message
5
+ from opensimula.Parameters import Parameter_options, Parameter_float, Parameter_float_list
6
+ from shapely.geometry import Polygon
7
+ from opensimula.Variable import Variable
8
+
9
+
10
+ class Surface(Component):
11
+ def __init__(self, name, project):
12
+ Component.__init__(self, name, project)
13
+ # Parameters
14
+ self.parameter("type").value = "Surface"
15
+ self.add_parameter(Parameter_float("azimuth", 0, "°", min=-180, max=180)) # Surface x vs Building x -> S: 0º, E: 90º, W: -90º, N: 180º
16
+ self.add_parameter(Parameter_float("altitude", 0, "°", min=-90, max=90)) # Surface y vs Building y -> vertical: 0º, facing up: 90º, facing down: -90º
17
+ self.add_parameter(Parameter_options(
18
+ "shape", "RECTANGLE", ["RECTANGLE", "POLYGON"]))
19
+ self.add_parameter(Parameter_float("width", 1, "m", min=0.0))
20
+ self.add_parameter(Parameter_float("height", 1, "m", min=0.0))
21
+ # Building Coordinate system
22
+ self.add_parameter(Parameter_float_list(
23
+ "ref_point", [0, 0, 0], "m", min=float("-inf")))
24
+ self.add_parameter(Parameter_float_list(
25
+ "x_polygon", [0, 10, 10, 0], "m", min=float("-inf")))
26
+ self.add_parameter(Parameter_float_list(
27
+ "y_polygon", [0, 0, 10, 10], "m", min=float("-inf")))
28
+
29
+ # Variables
30
+ self.add_variable(Variable("E_dir_sunny", "W/m²")) # Without shadows
31
+ self.add_variable(Variable("E_dir", "W/m²"))
32
+ self.add_variable(Variable("E_dif_sunny", "W/m²")) # Without shadows
33
+ self.add_variable(Variable("E_dif", "W/m²"))
34
+
35
+ def check(self):
36
+ errors = super().check()
37
+ # Test if Polygon shape that x_polygon and y_polygon has the same size
38
+ if self.parameter("shape").value == "POLYGON":
39
+ if len(self.parameter("x_polygon").value) != len(self.parameter("y_polygon").value):
40
+ msg = f"{self.parameter('name').value}, x_polygo and y_polygon must have the same size."
41
+ errors.append(Message(msg, "ERROR"))
42
+ return errors
43
+
44
+ def pre_simulation(self, n_time_steps, delta_t):
45
+ super().pre_simulation(n_time_steps, delta_t)
46
+
47
+ @property
48
+ def area(self):
49
+ if (self.parameter("shape").value == "RECTANGLE"):
50
+ return self.parameter("width").value * self.parameter("height").value
51
+ elif (self.parameter("shape").value == "POLYGON"):
52
+ polygon = []
53
+ n = len(self.parameter("x_polygon").value)
54
+ for i in range(0, n):
55
+ polygon.append([self.parameter("x_polygon").value[i],
56
+ self.parameter("y_polygon").value[i]])
57
+ return Polygon(polygon).area
58
+
59
+ # exterior normal vector
60
+ def orientation_angle(self, angle, side, coordinate_system="global"):
61
+ if angle == "azimuth":
62
+ az = self.parameter("azimuth").value
63
+ if self.get_building() is not None and coordinate_system == "global":
64
+ azi_building = self.get_building().parameter("azimuth").value
65
+ az = az + azi_building
66
+ if side == 0:
67
+ return az
68
+ elif side == 1:
69
+ if az > 0:
70
+ return az-180
71
+ else:
72
+ return az+180
73
+ elif angle == "altitude":
74
+ alt = self.parameter("altitude").value
75
+ if side == 0:
76
+ return alt
77
+ elif side == 1:
78
+ return -alt
79
+
80
+ def get_building(self): # Default no building
81
+ return None
82
+
83
+ def radiant_property(self, prop, radiation_type, side, theta=0):
84
+ """It must be redefined by child classes
85
+
86
+ Args:
87
+ prop (_type_): _description_
88
+ radiation_type (_type_): _description_
89
+ side (_type_): _description_
90
+
91
+ Returns:
92
+ _type_: _description_
93
+ """
94
+ return 0
95
+
96
+ def get_origin(self, coordinate_system="global"):
97
+ if self.get_building() is not None:
98
+ if coordinate_system == "global":
99
+ az_b = math.radians(self.get_building().parameter("azimuth").value)
100
+ local_origin = self.parameter("ref_point").value
101
+ global_origin = [local_origin[0]*math.cos(az_b)-local_origin[1]*math.sin(az_b),
102
+ local_origin[0]*math.sin(az_b) +
103
+ local_origin[1]*math.cos(az_b),
104
+ local_origin[2]]
105
+ return global_origin
106
+ elif coordinate_system == "building":
107
+ return np.array(self.parameter("ref_point").value)+ np.array(self.get_building().parameter("ref_point").value)
108
+ else:
109
+ return self.parameter("ref_point").value
110
+ else:
111
+ return self.parameter("ref_point").value
112
+
113
+ def get_polygon_2D(self): # Get polygon_2D
114
+ if (self.parameter("shape").value == "RECTANGLE"):
115
+ w = self.parameter("width").value
116
+ h = self.parameter("height").value
117
+ return [[0, 0], [w, 0], [w, h], [0, h]]
118
+ elif (self.parameter("shape").value == "POLYGON"):
119
+ polygon2D = []
120
+ n = len(self.parameter("x_polygon").value)
121
+ for i in range(0, n):
122
+ polygon2D.append([self.parameter("x_polygon").value[i],
123
+ self.parameter("y_polygon").value[i]])
124
+ return polygon2D
125
+
126
+ def get_polygon_3D(self): # Global coordenates
127
+ return None
128
+
129
+
@@ -0,0 +1,76 @@
1
+ from opensimula.Parameters import (
2
+ Parameter_boolean,
3
+ Parameter_float,
4
+ Parameter_int,
5
+ Parameter_string,
6
+ Parameter_options,
7
+ Parameter_component,
8
+ Parameter_boolean_list,
9
+ Parameter_string_list,
10
+ Parameter_int_list,
11
+ Parameter_float_list,
12
+ Parameter_options_list,
13
+ Parameter_component_list,
14
+ Parameter_variable,
15
+ Parameter_variable_list,
16
+ Parameter_math_exp,
17
+ Parameter_math_exp_list
18
+ )
19
+ from opensimula.Component import Component
20
+ from opensimula.Variable import Variable
21
+
22
+
23
+ class Test_component(Component):
24
+ """Component for development testing"""
25
+
26
+ def __init__(self, name, project):
27
+ Component.__init__(self, name, project)
28
+ self.parameter("type").value = "Test_component"
29
+ self.parameter("description").value = "Dummy component for testing"
30
+
31
+ self.add_parameter(Parameter_boolean("boolean", False))
32
+ self.add_parameter(Parameter_string("string", "Hello World"))
33
+ self.add_parameter(Parameter_int("int", 100, "h"))
34
+ self.add_parameter(Parameter_float("float", 0.1, "m"))
35
+ self.add_parameter(Parameter_options(
36
+ "options", "One", ["One", "Two", "Three"]))
37
+ self.add_parameter(Parameter_component("component", "not_defined"))
38
+ self.add_parameter(Parameter_variable("variable"))
39
+ self.add_parameter(Parameter_math_exp("math_exp"))
40
+ self.add_parameter(Parameter_boolean_list(
41
+ "boolean_list", [True, False]))
42
+ self.add_parameter(Parameter_string_list(
43
+ "string_list", ["Hello World 1", "Hello World 2"]))
44
+ self.add_parameter(Parameter_int_list("int_list", [50, 100], "h"))
45
+ self.add_parameter(Parameter_float_list("float_list", [0.1, 0.2], "m"))
46
+ self.add_parameter(Parameter_options_list(
47
+ "options_list", ["One", "Two"], ["One", "Two", "Three"]))
48
+ self.add_parameter(Parameter_component_list(
49
+ "component_list", ["not_defined", "not_defined"]))
50
+ self.add_parameter(Parameter_variable_list("variable_list"))
51
+ self.add_parameter(Parameter_math_exp_list("math_exp_list"))
52
+
53
+ # Variable
54
+ self.add_variable(Variable("t", unit="s"))
55
+ self.add_variable(Variable("exp_1"))
56
+ self.add_variable(Variable("exp_2"))
57
+ self.add_variable(Variable("exp_3"))
58
+
59
+ self._initial_date_ = None
60
+
61
+ def pre_simulation(self, n_time_steps, delta_t):
62
+ super().pre_simulation(n_time_steps, delta_t)
63
+
64
+ def pre_iteration(self, time_index, date, daylight_saving):
65
+ super().pre_iteration(time_index, date, daylight_saving)
66
+ if time_index == 0:
67
+ self._initial_date_ = date
68
+ self.variable("t").values[time_index] = (
69
+ date - self._initial_date_).total_seconds()
70
+
71
+ variables_dic = {}
72
+ for key, var in self._variables_.items():
73
+ variables_dic[key] = var.values[time_index]
74
+
75
+ self.variable("exp_1").values[time_index] = self.parameter(
76
+ "math_exp").evaluate(variables_dic)
@@ -0,0 +1,30 @@
1
+ from opensimula.Message import Message
2
+ from opensimula.Parameters import Parameter_component_list
3
+ from opensimula.Component import Component
4
+
5
+
6
+ class Week_schedule(Component):
7
+ def __init__(self, name, project):
8
+ Component.__init__(self, name, project)
9
+ self.parameter("type").value = "Week_schedule"
10
+ self.parameter("description").value = "Time schedule for a week"
11
+ self.add_parameter(Parameter_component_list(
12
+ "days_schedules", ["not_defined"], ["Day_schedule"]))
13
+
14
+ def check(self):
15
+ errors = super().check()
16
+ # Test if 1 or 7 has been defined
17
+ if (
18
+ len(self.parameter("days_schedules").value) != 1
19
+ and len(self.parameter("days_schedules").value) != 7
20
+ ):
21
+ msg = f"{self.parameter('name').value}, days_schedules parameter must contain 1 or 7 Day_schedule components"
22
+ errors.append(Message(msg, "ERROR"))
23
+ return errors
24
+
25
+ def get_value(self, date):
26
+ if len(self.parameter("days_schedules").value) == 1:
27
+ return self.parameter("days_schedules").component[0].get_value(date)
28
+ else:
29
+ index = date.weekday()
30
+ return self.parameter("days_schedules").component[index].get_value(date)
@@ -0,0 +1,65 @@
1
+ import datetime as dt
2
+ from bisect import bisect
3
+ from opensimula.Message import Message
4
+ from opensimula.Parameters import Parameter_component_list, Parameter_string_list
5
+ from opensimula.Component import Component
6
+ from opensimula.Variable import Variable
7
+
8
+
9
+ class Year_schedule(Component):
10
+ def __init__(self, name, project):
11
+ Component.__init__(self, name, project)
12
+ self.parameter("type").value = "Year_schedule"
13
+ self.parameter("description").value = "Time schedule for a year"
14
+ self.add_parameter(Parameter_string_list("periods", ["01/06"]))
15
+ self.add_parameter(
16
+ Parameter_component_list(
17
+ "weeks_schedules", ["not_defined",
18
+ "not_defined"], ["Week_schedule"]
19
+ )
20
+ )
21
+ # Create Variable
22
+ self.add_variable(Variable(
23
+ "values", "", "Values obtained using year, weeks and days schedules for each of the simulation time steps."))
24
+
25
+ def check(self):
26
+ errors = super().check()
27
+ if (
28
+ len(self.parameter("periods").value)
29
+ != len(self.parameter("weeks_schedules").value) - 1
30
+ ):
31
+ msg = f"{self.parameter('name').value}, periods size must be weeks_schedules size minus 1"
32
+ errors.append(Message(msg, "ERROR"))
33
+ # Check periods format
34
+ try:
35
+ days = []
36
+ for period in self.parameter("periods").value:
37
+ datetime = dt.datetime.strptime(period, "%d/%m")
38
+ days.append(datetime.timetuple().tm_yday)
39
+ if sorted(days) != days:
40
+ msg = f"{self.parameter('name').value}, periods are not ordered"
41
+ errors.append(Message(msg, "ERROR"))
42
+ except ValueError:
43
+ msg = f"{self.parameter('name').value}, periods does not match format (dd/mm)"
44
+ errors.append(Message(msg, "ERROR"))
45
+ return errors
46
+
47
+ def pre_simulation(self, n_time_steps, delta_t):
48
+ super().pre_simulation(n_time_steps, delta_t)
49
+
50
+ # Create array of periods_days
51
+ self._periods_days_ = []
52
+ for period in self.parameter("periods").value:
53
+ datetime = dt.datetime.strptime(period, "%d/%m")
54
+ self._periods_days_.append(datetime.timetuple().tm_yday)
55
+
56
+ def pre_iteration(self, time_index, date, daylight_saving):
57
+ super().pre_iteration(time_index, date, daylight_saving)
58
+ if daylight_saving: # adding 1 h
59
+ date = date + dt.timedelta(0, 3600)
60
+ self.variable("values").values[time_index] = self.get_value(date)
61
+
62
+ def get_value(self, date):
63
+ year_day = date.timetuple().tm_yday
64
+ index = bisect(self._periods_days_, year_day)
65
+ return self.parameter("weeks_schedules").component[index].get_value(date)
@@ -0,0 +1,40 @@
1
+ from opensimula.components.Test_component import Test_component
2
+ from opensimula.components.Material import Material
3
+ from opensimula.components.Glazing import Glazing
4
+ from opensimula.components.Frame import Frame
5
+ from opensimula.components.Construction import Construction
6
+ from opensimula.components.Opening_type import Opening_type
7
+ from opensimula.components.File_data import File_data
8
+ from opensimula.components.Day_schedule import Day_schedule
9
+ from opensimula.components.Week_schedule import Week_schedule
10
+ from opensimula.components.Year_schedule import Year_schedule
11
+ from opensimula.components.File_met import File_met
12
+ from opensimula.components.Space_type import Space_type
13
+ from opensimula.components.Building import Building
14
+ from opensimula.components.Space import Space
15
+ from opensimula.components.Building_surface import Building_surface
16
+ from opensimula.components.Solar_surface import Solar_surface
17
+ from opensimula.components.Opening import Opening
18
+ from opensimula.components.Calculator import Calculator
19
+ from opensimula.components.HVAC_DX_equipment import HVAC_DX_equipment
20
+ from opensimula.components.HVAC_coil_equipment import HVAC_coil_equipment
21
+ from opensimula.components.HVAC_fan_equipment import HVAC_fan_equipment
22
+ from opensimula.components.HVAC_DX_system import HVAC_DX_system
23
+ from opensimula.components.HVAC_SZW_system import HVAC_SZW_system
24
+ from opensimula.components.HVAC_MZW_system import HVAC_MZW_system
25
+ from opensimula.components.HVAC_perfect_system import HVAC_perfect_system
26
+
27
+ DEFAULT_COMPONENTS_ORDER = [
28
+ "Space_type",
29
+ "Building_surface",
30
+ "Solar_surface",
31
+ "Opening",
32
+ "Space",
33
+ "Building",
34
+ "HVAC_MZW_system",
35
+ "HVAC_perfect_system",
36
+ "HVAC_DX_system",
37
+ "HVAC_SZW_system",
38
+ "Calculator"
39
+ ]
40
+
@@ -0,0 +1,257 @@
1
+ import time
2
+ import numpy as np
3
+ import math
4
+ from scipy.interpolate import RegularGridInterpolator
5
+ import matplotlib.pyplot as plt
6
+ import vedo as vedo
7
+
8
+ class Environment_3D:
9
+ def __init__(self):
10
+ self.pol_3D = []
11
+ self.pol_sunny = []
12
+ self.pol_shadows = []
13
+ self.sunny_fraction = []
14
+ self.solar_tables_calculated = False
15
+
16
+ def add_polygon_3D(self, polygon_3D):
17
+ self.pol_3D.append(polygon_3D)
18
+ self.sunny_fraction.append(1.0)
19
+
20
+ def get_vedo_meshes(self, polygons_type="initial"):
21
+ meshes = []
22
+ if polygons_type == "initial":
23
+ for polygon_3D in self.pol_3D:
24
+ if polygon_3D.visible:
25
+ mesh = polygon_3D.get_vedo_mesh()
26
+ mesh.polygon_name = polygon_3D.name
27
+ meshes.append(mesh)
28
+ meshes.append(mesh.silhouette("2d").c("black").linewidth(5))
29
+ elif polygons_type == "sunny":
30
+ for polygon_3D in self.pol_sunny:
31
+ mesh = polygon_3D.get_vedo_mesh()
32
+ mesh.polygon_name = polygon_3D.name
33
+ meshes.append(mesh)
34
+ meshes.append(mesh.silhouette("2d").c("black").linewidth(5))
35
+ elif polygons_type == "shadows":
36
+ for polygon_3D in self.pol_shadows:
37
+ mesh = polygon_3D.get_vedo_mesh()
38
+ mesh.polygon_name = polygon_3D.name
39
+ meshes.append(mesh)
40
+ meshes.append(mesh.silhouette("2d").c("black").linewidth(5))
41
+ elif polygons_type == "sunny+shadows":
42
+ for polygon_3D in self.pol_sunny:
43
+ mesh = polygon_3D.get_vedo_mesh()
44
+ mesh.polygon_name = polygon_3D.name
45
+ meshes.append(mesh)
46
+ meshes.append(mesh.silhouette("2d").c("black").linewidth(5))
47
+ for polygon_3D in self.pol_shadows:
48
+ mesh = polygon_3D.get_vedo_mesh()
49
+ mesh.polygon_name = polygon_3D.name
50
+ meshes.append(mesh)
51
+ meshes.append(mesh.silhouette("2d").c("black").linewidth(5))
52
+ elif polygons_type == "Building_shadows":
53
+ for polygon_3D in self.pol_sunny:
54
+ mesh = polygon_3D.get_vedo_mesh()
55
+ mesh.polygon_name = polygon_3D.name
56
+ meshes.append(mesh)
57
+ meshes.append(mesh.silhouette("2d").c("black").linewidth(5))
58
+ for polygon_3D in self.pol_shadows:
59
+ mesh = polygon_3D.get_vedo_mesh()
60
+ mesh.polygon_name = polygon_3D.name
61
+ meshes.append(mesh)
62
+ meshes.append(mesh.silhouette("2d").c("black").linewidth(5))
63
+ for polygon_3D in self.pol_3D:
64
+ if polygon_3D.visible and polygon_3D.calculate_shadows == False:
65
+ mesh = polygon_3D.get_vedo_mesh()
66
+ mesh.polygon_name = polygon_3D.name
67
+ meshes.append(mesh)
68
+ meshes.append(mesh.silhouette("2d").c("black").linewidth(5))
69
+
70
+ return meshes
71
+
72
+ def show(self, polygons_type="initial"):
73
+ vedo.settings.default_backend = 'vtk'
74
+ meshes = self.get_vedo_meshes(polygons_type)
75
+
76
+ text_obj = [None] # Usamos una lista para que sea mutable en el callback
77
+
78
+ def on_left_click(evt):
79
+ if text_obj[0] is not None:
80
+ plt.remove(text_obj[0]) # Borra el texto anterior
81
+ msh = evt.object
82
+ if not msh:
83
+ text_obj[0] = vedo.Text2D(" ", pos='top-left')
84
+ else:
85
+ text_obj[0] = vedo.Text2D(f"{msh.polygon_name}", pos='top-left')
86
+ plt.add(text_obj[0])
87
+ plt.render()
88
+
89
+ plt = vedo.Plotter(title="opensimula")
90
+ plt.add_callback('mouse click', on_left_click)
91
+ plt.show(*meshes, axes=1, viewup="z").close()
92
+
93
+ def show_animation(self, texts, cosines , polygons_type="initial" ):
94
+ vedo.settings.default_backend = 'vtk'
95
+ meshes = []
96
+ def slider_func(widget, event):
97
+ idx = int(widget.value)
98
+ widget.title = texts[idx]
99
+ plt.remove( meshes.pop(0))
100
+ cos = cosines[idx]
101
+ self.calculate_shadows(cos, create_polygons=True)
102
+ meshes.append(self.get_vedo_meshes(polygons_type))
103
+ plt.add(meshes[0])
104
+
105
+ # Show first frame
106
+ self.calculate_shadows(cosines[0], create_polygons=True)
107
+ meshes.append(self.get_vedo_meshes(polygons_type))
108
+ plt = vedo.Plotter(title="opensimula", axes=1)
109
+ plt.add(meshes[0])
110
+ plt.add_slider(
111
+ slider_func,
112
+ 0, len(cosines)-1, # slider range
113
+ value=0, # initial value
114
+ pos="bottom-right", # position of the slider
115
+ title=texts[0]
116
+ )
117
+ plt.show(viewup="z").close()
118
+
119
+ def delete_shadows(self):
120
+ self.pol_sunny = []
121
+ self.pol_shadows = []
122
+ self.sunny_fraction = []
123
+
124
+ def calculate_shadows(self, sun_position, create_polygons=True):
125
+ self.sunny_fraction = []
126
+ if create_polygons:
127
+ self.pol_sunny = []
128
+ self.pol_shadows = []
129
+
130
+ for polygon in self.pol_3D:
131
+ if polygon.calculate_shadows:
132
+ sunny_polygons, shadow_polygons = polygon.get_sunny_shadow_polygon3D(
133
+ self, sun_position
134
+ )
135
+ if sunny_polygons is not None:
136
+ sunny_area = 0
137
+ for sunny_polygon in sunny_polygons:
138
+ if create_polygons:
139
+ self.pol_sunny.append(sunny_polygon)
140
+ sunny_area += sunny_polygon.area
141
+ self.sunny_fraction.append(sunny_area / polygon.area)
142
+ else:
143
+ self.sunny_fraction.append(0) # No sunny area
144
+ if create_polygons:
145
+ if shadow_polygons is not None:
146
+ for shadow_polygon in shadow_polygons:
147
+ self.pol_shadows.append(shadow_polygon)
148
+
149
+ def get_sunny_polygon_list(self):
150
+ sunny_polygons = []
151
+ for polygon in self.pol_3D:
152
+ if polygon.calculate_shadows:
153
+ sunny_polygons.append(polygon)
154
+ return sunny_polygons
155
+
156
+ def get_sunny_index(self, name):
157
+ sunny_polygons = self.get_sunny_polygon_list()
158
+ for i in range(len(sunny_polygons)):
159
+ if sunny_polygons[i].name == name:
160
+ return i
161
+ return None
162
+
163
+ def calculate_solar_tables(self):
164
+ self._calculate_shadow_interpolation_table()
165
+ self._calculate_diffuse_shadow()
166
+ self.solar_tables_calculated = True
167
+
168
+ def _calculate_shadow_interpolation_table(self):
169
+ self.shadow_azimuth_grid = np.linspace(0, 350, 36)
170
+ self.shadow_altitude_grid = np.linspace(-85, 85, 18)
171
+ sunny_polygons = self.get_sunny_polygon_list()
172
+ self.sunny_fraction_tables = np.zeros((len(sunny_polygons), 36, 18))
173
+ j = 0
174
+ for azimuth in self.shadow_azimuth_grid:
175
+ azi_rd = math.radians(azimuth)
176
+ k = 0
177
+ for altitude in self.shadow_altitude_grid:
178
+ alt_rd = math.radians(altitude)
179
+ sun_position = np.array(
180
+ [
181
+ math.cos(alt_rd) * math.sin(azi_rd),
182
+ -math.cos(alt_rd) * math.cos(azi_rd),
183
+ math.sin(alt_rd),
184
+ ]
185
+ )
186
+
187
+ self.calculate_shadows(sun_position, create_polygons=False)
188
+ sunny_frac = self.sunny_fraction
189
+ for i in range(len(sunny_frac)):
190
+ self.sunny_fraction_tables[i][j][k] = sunny_frac[i]
191
+ k = k + 1
192
+ j = j + 1
193
+
194
+ self.sunny_interpolation_functions = []
195
+ for i in range(0, len(sunny_polygons)):
196
+ self.sunny_interpolation_functions.append(
197
+ RegularGridInterpolator(
198
+ (self.shadow_azimuth_grid, self.shadow_altitude_grid),
199
+ self.sunny_fraction_tables[i],
200
+ bounds_error=False,
201
+ fill_value=None,
202
+ method="cubic",
203
+ )
204
+ )
205
+
206
+ def _calculate_diffuse_shadow(self):
207
+ def integral(i,polygon_i):
208
+ sunny_value = 0
209
+ shadow_value = 0
210
+ n = 0
211
+ for j in range(len(self.shadow_azimuth_grid)):
212
+ for k in range(len(self.shadow_altitude_grid)):
213
+ theta = polygon_i.get_angle_with_normal(
214
+ self.shadow_azimuth_grid[j], self.shadow_altitude_grid[k]
215
+ )
216
+ if theta < math.pi / 2:
217
+ f = 0.5 * math.sin(2 * theta)
218
+ sunny_value = sunny_value + f
219
+ shadow_value = (
220
+ shadow_value + f * self.sunny_fraction_tables[i][j][k]
221
+ )
222
+ n = n + 1
223
+ return shadow_value / sunny_value
224
+
225
+ self.shadow_diffuse_fraction = []
226
+ sunny_polygon_list = self.get_sunny_polygon_list()
227
+ for i in range(0, len(sunny_polygon_list)):
228
+ polygon_i = sunny_polygon_list[i]
229
+ self.shadow_diffuse_fraction.append(integral(i, polygon_i))
230
+
231
+ def get_diffuse_sunny_fraction(self, sunny_i):
232
+ if self.solar_tables_calculated == True:
233
+ return self.shadow_diffuse_fraction[sunny_i]
234
+ else:
235
+ return 1
236
+
237
+ def get_direct_interpolated_sunny_fraction(self, sunny_i, azi, alt):
238
+ if self.solar_tables_calculated == True:
239
+ if azi < 0:
240
+ azi = azi + 360
241
+ return self.sunny_interpolation_functions[sunny_i]((azi, alt))
242
+ else:
243
+ return 1
244
+
245
+ def get_direct_sunny_fraction(self, sunny_i):
246
+ if self.sunny_fraction == []:
247
+ return 1
248
+ else:
249
+ return self.sunny_fraction[sunny_i]
250
+
251
+ def show_sunny_fraction(self, sunny_i):
252
+ fig, ax = plt.subplots()
253
+ X, Y = np.meshgrid(self.shadow_azimuth_grid, self.shadow_altitude_grid)
254
+ ax.imshow(self.sunny_fraction_tables[sunny_i], vmin=0, vmax=1)
255
+ plt.show()
256
+
257
+