openstudio-ee 0.12.3 → 0.12.5
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.
- checksums.yaml +4 -4
- data/.coverage +0 -0
- data/.github/workflows/test-with-openstudio.yml +109 -74
- data/.gitignore +21 -0
- data/.rubocop.yml +15 -2
- data/CHANGELOG.md +6 -0
- data/Gemfile +7 -8
- data/README.md +3 -2
- data/WORKFLOW_CHANGES.md +74 -0
- data/lib/measures/AddDaylightSensors/measure.rb +79 -79
- data/lib/measures/AddDaylightSensors/measure.xml +4 -4
- data/lib/measures/AddOverhangsByProjectionFactor/measure.rb +38 -41
- data/lib/measures/AddOverhangsByProjectionFactor/measure.xml +4 -4
- data/lib/measures/EnableDemandControlledVentilation/measure.rb +37 -40
- data/lib/measures/EnableDemandControlledVentilation/measure.xml +4 -4
- data/lib/measures/EnableEconomizerControl/measure.rb +36 -37
- data/lib/measures/EnableEconomizerControl/measure.xml +4 -4
- data/lib/measures/GLHEProExportLoadsforGroundHeatExchangerSizing/measure.rb +27 -41
- data/lib/measures/GLHEProExportLoadsforGroundHeatExchangerSizing/measure.xml +4 -4
- data/lib/measures/GLHEProGFunctionImport/measure.rb +11 -15
- data/lib/measures/GLHEProGFunctionImport/measure.xml +4 -4
- data/lib/measures/GLHEProSetupExportLoadsforGroundHeatExchangerSizing/measure.rb +5 -9
- data/lib/measures/GLHEProSetupExportLoadsforGroundHeatExchangerSizing/measure.xml +3 -3
- data/lib/measures/ImproveFanBeltEfficiency/measure.rb +78 -95
- data/lib/measures/ImproveFanBeltEfficiency/measure.xml +6 -6
- data/lib/measures/ImproveMotorEfficiency/measure.rb +75 -100
- data/lib/measures/ImproveMotorEfficiency/measure.xml +6 -6
- data/lib/measures/IncreaseInsulationRValueForExteriorWalls/measure.rb +137 -130
- data/lib/measures/IncreaseInsulationRValueForExteriorWalls/measure.xml +4 -4
- data/lib/measures/IncreaseInsulationRValueForExteriorWallsByPercentage/measure.rb +114 -115
- data/lib/measures/IncreaseInsulationRValueForExteriorWallsByPercentage/measure.xml +3 -3
- data/lib/measures/IncreaseInsulationRValueForRoofs/measure.rb +137 -130
- data/lib/measures/IncreaseInsulationRValueForRoofs/measure.xml +4 -4
- data/lib/measures/IncreaseInsulationRValueForRoofsByPercentage/measure.rb +114 -115
- data/lib/measures/IncreaseInsulationRValueForRoofsByPercentage/measure.xml +3 -3
- data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/measure.rb +69 -63
- data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/measure.xml +6 -6
- data/lib/measures/ReduceLightingLoadsByPercentage/measure.rb +77 -66
- data/lib/measures/ReduceLightingLoadsByPercentage/measure.xml +6 -6
- data/lib/measures/ReduceNightTimeElectricEquipmentLoads/measure.rb +45 -43
- data/lib/measures/ReduceNightTimeElectricEquipmentLoads/measure.xml +4 -4
- data/lib/measures/ReduceNightTimeLightingLoads/measure.rb +45 -43
- data/lib/measures/ReduceNightTimeLightingLoads/measure.xml +4 -4
- data/lib/measures/ReduceSpaceInfiltrationByPercentage/measure.rb +58 -52
- data/lib/measures/ReduceSpaceInfiltrationByPercentage/measure.xml +6 -6
- data/lib/measures/ReduceVentilationByPercentage/measure.rb +49 -46
- data/lib/measures/ReduceVentilationByPercentage/measure.xml +6 -6
- data/lib/measures/add_variable_speed_rtu_control_logic/measure.rb +31 -23
- data/lib/measures/add_variable_speed_rtu_control_logic/measure.xml +4 -4
- data/lib/measures/create_variable_speed_rtu/measure.rb +166 -174
- data/lib/measures/create_variable_speed_rtu/measure.xml +6 -6
- data/lib/measures/fan_assist_night_ventilation/measure.rb +33 -32
- data/lib/measures/fan_assist_night_ventilation/measure.xml +4 -4
- data/lib/measures/nze_hvac/measure.rb +72 -62
- data/lib/measures/nze_hvac/measure.xml +4 -4
- data/lib/measures/replace_water_heater_mixed_with_thermal_storage_chilled_water/measure.rb +16 -19
- data/lib/measures/replace_water_heater_mixed_with_thermal_storage_chilled_water/measure.xml +4 -4
- data/lib/measures/window_enhancement/LICENSE.md +14 -0
- data/lib/measures/window_enhancement/README.md +112 -0
- data/lib/measures/window_enhancement/docs/.gitkeep +0 -0
- data/lib/measures/window_enhancement/measure.py +386 -0
- data/lib/measures/window_enhancement/measure.xml +271 -0
- data/lib/measures/window_enhancement/resources/EC3_lookup.py +321 -0
- data/lib/measures/window_enhancement/resources/Test_API.py +32 -0
- data/lib/measures/window_enhancement/resources/__pycache__/EC3_lookup.cpython-39.pyc +0 -0
- data/lib/measures/window_enhancement/resources/__pycache__/Original_EC3_lookup.py +322 -0
- data/lib/measures/window_enhancement/resources/__pycache__/Test_API.cpython-39.pyc +0 -0
- data/lib/measures/window_enhancement/resources/calculate_perimeter.py +39 -0
- data/lib/measures/window_enhancement/test_output.log +39 -0
- data/lib/openstudio/ee_measures/version.rb +1 -1
- data/openstudio-ee.gemspec +10 -8
- data/test-workflow-locally.sh +152 -0
- metadata +64 -35
- data/Jenkinsfile +0 -11
@@ -0,0 +1,322 @@
|
|
1
|
+
# EC3 API Call
|
2
|
+
import requests
|
3
|
+
import json
|
4
|
+
import re
|
5
|
+
import pprint
|
6
|
+
import statistics
|
7
|
+
|
8
|
+
print("EC3 API Call:")
|
9
|
+
|
10
|
+
EC3_category_array = {
|
11
|
+
"ConstructionMaterials": {
|
12
|
+
"Concrete": [
|
13
|
+
"ReadyMix",
|
14
|
+
"Shotcrete",
|
15
|
+
"CementGrout",
|
16
|
+
"FlowableFill",
|
17
|
+
"Precast",
|
18
|
+
"Rebar",
|
19
|
+
"WireMeshSteel",
|
20
|
+
"SCM",
|
21
|
+
"Cement",
|
22
|
+
"Aggregates"
|
23
|
+
],
|
24
|
+
"Masonry": [
|
25
|
+
"Brick",
|
26
|
+
"ConcreteUnitMasonry",
|
27
|
+
"Mortar",
|
28
|
+
"Cementitious",
|
29
|
+
"Aggregates"
|
30
|
+
],
|
31
|
+
"Steel": [],
|
32
|
+
"Aluminum": [],
|
33
|
+
"Wood": [],
|
34
|
+
"Sheathing": [],
|
35
|
+
"ThermalMoistureProtection": [],
|
36
|
+
"Cladding": [],
|
37
|
+
"Openings": [],
|
38
|
+
"Finishes": [],
|
39
|
+
"ConveyingEquipment": [],
|
40
|
+
"NetworkInfrastrucure": [],
|
41
|
+
"Asphalt": [],
|
42
|
+
"Accessories": [],
|
43
|
+
"ManufacturingInputs": [],
|
44
|
+
"BulkMaterials": [],
|
45
|
+
"Placeholders": []
|
46
|
+
},
|
47
|
+
"BuildingAssemblies": {
|
48
|
+
"NewCoustomAssembly": [],
|
49
|
+
"ReinforcedConcrete": [],
|
50
|
+
"Walls": [],
|
51
|
+
"Floors": [],
|
52
|
+
"GlazingFenesration": [],
|
53
|
+
},
|
54
|
+
}
|
55
|
+
|
56
|
+
# Define the URL with query parameters (FIXME: Not including API key in public repo. Will need to be included in a gitignored config.ini file)
|
57
|
+
windows_url = (
|
58
|
+
"https://api.buildingtransparency.org/api/epds"
|
59
|
+
"?page_number=1&page_size=25&fields=id%2Copen_xpd_uuid%2Cis_failed%2Cfailures%2Cerrors%2Cwarnings"
|
60
|
+
"%2Cdate_validity_ends%2Ccqd_sync_unlocked%2Cmy_capabilities%2Coriginal_data_format%2Ccategory"
|
61
|
+
"%2Cdisplay_name%2Cmanufacturer%2Cplant_or_group%2Cname%2Cdescription%2Cprogram_operator"
|
62
|
+
"%2Cprogram_operator_fkey%2Cverifier%2Cdeveloper%2Cmatched_plants_count%2Cplant_geography%2Cpcr"
|
63
|
+
"%2Cshort_name%2Cversion%2Cdate_of_issue%2Clanguage%2Cgwp%2Cuncertainty_adjusted_gwp%2Cdeclared_unit"
|
64
|
+
"%2Cupdated_on%2Ccorrections_count%2Cdeclaration_type%2Cbox_id%2Cis_downloadable"
|
65
|
+
"&sort_by=-updated_on&name__like=window&description__like=window&q=windows&plant_geography=US&declaration_type=Product%20EPD"
|
66
|
+
)
|
67
|
+
|
68
|
+
igu_url = (
|
69
|
+
"https://api.buildingtransparency.org/api/materials"
|
70
|
+
"?page_number=1&page_size=100"
|
71
|
+
"&mf=!EC3%20search(%22InsulatingGlazingUnits%22)%20WHERE%20"
|
72
|
+
"%0A%20%20jurisdiction%3A%20IN(%22021%22)%20AND%0A%20%20"
|
73
|
+
"epd__date_validity_ends%3A%20%3E%20%222024-12-05%22%20AND%0A%20%20"
|
74
|
+
"epd_types%3A%20IN(%22Product%20EPDs%22)%20"
|
75
|
+
"!pragma%20eMF(%222.0%2F1%22)%2C%20lcia(%22TRACI%202.1%22)"
|
76
|
+
)
|
77
|
+
|
78
|
+
wframe_url = (
|
79
|
+
"https://api.buildingtransparency.org/api/materials"
|
80
|
+
"?page_number=1&page_size=25"
|
81
|
+
"&mf=!EC3%20search(%22AluminiumExtrusions%22)%20WHERE%20"
|
82
|
+
"%0A%20%20jurisdiction%3A%20IN(%22021%22)%20AND%0A%20%20"
|
83
|
+
"epd__date_validity_ends%3A%20%3E%20%222024-12-09%22%20AND%0A%20%20"
|
84
|
+
"epd_types%3A%20IN(%22Product%20EPDs%22)%20"
|
85
|
+
"!pragma%20eMF(%222.0%2F1%22)%2C%20lcia(%22TRACI%202.1%22)"
|
86
|
+
)
|
87
|
+
|
88
|
+
|
89
|
+
gypsum_url = (
|
90
|
+
"https://api.buildingtransparency.org/api/materials"
|
91
|
+
"?page_number=1&page_size=25"
|
92
|
+
"&mf=!EC3%20search(%22Gypsum%22)%20WHERE%20"
|
93
|
+
"%0A%20%20jurisdiction%3A%20IN(%22US%22%2C%20%22CA%22)%20AND%0A%20%20"
|
94
|
+
"epd__date_validity_ends%3A%20%3E%20%222024-12-24%22%20AND%0A%20%20"
|
95
|
+
"epd_types%3A%20IN(%22Product%20EPDs%22)%20AND%0A%20%20"
|
96
|
+
"gypsum_fire_rating%3A%20IN(%22X%22)%20AND%0A%20%20"
|
97
|
+
"gypsum_thickness%3A%20IN(%220.625%20in%22)%20AND%0A%20%20"
|
98
|
+
"gypsum_facing%3A%20IN(%22Paper%22)%20"
|
99
|
+
"!pragma%20eMF(%222.0%2F1%22)%2C%20lcia(%22TRACI%202.1%22)"
|
100
|
+
)
|
101
|
+
|
102
|
+
# Headers for the request
|
103
|
+
headers = {
|
104
|
+
"Accept": "application/json",
|
105
|
+
"Authorization": "Bearer 5fk7wP4cJg6pcmx6ncZN0ftMdoVR8u"
|
106
|
+
}
|
107
|
+
|
108
|
+
# Execute the GET request
|
109
|
+
igu_response = requests.get(igu_url, headers=headers, verify=False)
|
110
|
+
wframe_response = requests.get(wframe_url, headers=headers, verify=False)
|
111
|
+
|
112
|
+
# Parse the JSON response
|
113
|
+
igu_response = igu_response.json()
|
114
|
+
wframe_response = wframe_response.json()
|
115
|
+
#print(igu_response)
|
116
|
+
# Print the response and the number of EPDs
|
117
|
+
print(f"Number of EPDs for IGU: {len(igu_response)}")
|
118
|
+
print(f"Number of EPDs for window frame: {len(wframe_response)}")
|
119
|
+
|
120
|
+
# Exception for EPD: 0
|
121
|
+
if len(igu_response) == 0:
|
122
|
+
print("No EPD returned for IGU, please change the search terms")
|
123
|
+
if len(wframe_response) == 0:
|
124
|
+
print("No EPD returned for window frame, please change the search terms")
|
125
|
+
|
126
|
+
# List of keys to exclude
|
127
|
+
exclude_keys = [
|
128
|
+
'manufacturer',
|
129
|
+
'plant_or_group',
|
130
|
+
'category',
|
131
|
+
'created_on',
|
132
|
+
'updated_on',
|
133
|
+
'gwp',
|
134
|
+
'gwp_z',
|
135
|
+
'pct80_gwp_per_category_declared_unit',
|
136
|
+
'conservative_estimate',
|
137
|
+
'best_practice',
|
138
|
+
'standard_deviation',
|
139
|
+
'uncertainty_factor',
|
140
|
+
'uncertainty_adjusted_gwp',
|
141
|
+
'lowest_plausible_gwp',
|
142
|
+
]
|
143
|
+
|
144
|
+
# Create an empty list to store EPDs of IGU
|
145
|
+
igu_epd_data = {}
|
146
|
+
igu_gwp_per_volume = []
|
147
|
+
igu_gwp_per_mass = []
|
148
|
+
igu_gwp_per_area = []
|
149
|
+
wframe_epd_data = {}
|
150
|
+
wframe_gwp_per_volume = []
|
151
|
+
wframe_gwp_per_mass = []
|
152
|
+
wframe_gwp_per_area = []
|
153
|
+
|
154
|
+
for igu_epd_no, igu_epd in enumerate(igu_response, start=1):
|
155
|
+
print(f"========================================EPD No. {igu_epd_no}:==========================================")
|
156
|
+
# Initialize gwp per unit volume of IGU
|
157
|
+
gwp_per_unit_volume = "Not specified"
|
158
|
+
gwp_per_unit_area = "Not specified"
|
159
|
+
gwp_per_unit_mass = "Not specified"
|
160
|
+
# Create an empty list to store each key,value pair in an individual EPD object
|
161
|
+
igu_object = {}
|
162
|
+
|
163
|
+
for key, value in igu_epd.items():
|
164
|
+
if value is not None and key not in exclude_keys:
|
165
|
+
# Append the key-value pair to the list
|
166
|
+
igu_object[key] = value
|
167
|
+
# Extract the relevant keys
|
168
|
+
declared_unit = igu_epd.get("declared_unit")
|
169
|
+
thickness_unit = igu_epd.get("thickness")
|
170
|
+
gwp_per_category_declared_unit = igu_epd.get("gwp_per_category_declared_unit")
|
171
|
+
warnings = igu_epd.get("warnings")
|
172
|
+
mass_per_declared_unit = igu_epd.get("mass_per_declared_unit")
|
173
|
+
density_unit = igu_epd.get("density")
|
174
|
+
gwp_per_unit_mass = igu_epd.get("gwp_per_kg")
|
175
|
+
|
176
|
+
# Per volume
|
177
|
+
if declared_unit and "m3" in declared_unit:
|
178
|
+
gwp_per_category_declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_category_declared_unit)).group())
|
179
|
+
declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(declared_unit)).group())
|
180
|
+
gwp_per_unit_volume = gwp_per_category_declared_unit_value/declared_unit_value
|
181
|
+
|
182
|
+
elif declared_unit and "m2" in declared_unit and thickness_unit and "mm" in str(thickness_unit):
|
183
|
+
thickness = float(re.search(r"[-+]?\d*\.?\d+", thickness_unit).group()) # Extract numeric part
|
184
|
+
gwp_per_category_declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_category_declared_unit)).group())
|
185
|
+
thickness = thickness * 1e-3 # Convert thickness to meters
|
186
|
+
gwp_per_unit_volume = round(gwp_per_category_declared_unit_value / thickness,2)
|
187
|
+
|
188
|
+
elif density_unit and gwp_per_unit_mass:
|
189
|
+
density = float(re.search(r"[-+]?\d*\.?\d+", density_unit).group()) # Extract numeric part
|
190
|
+
gwp_per_unit_mass = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_unit_mass)).group())
|
191
|
+
gwp_per_unit_volume = gwp_per_unit_mass*density
|
192
|
+
gwp_per_unit_volume = round(gwp_per_unit_volume,2)
|
193
|
+
|
194
|
+
elif declared_unit and "t" in declared_unit and density_unit:
|
195
|
+
density = float(re.search(r"[-+]?\d*\.?\d+", density_unit).group()) # Extract numeric part
|
196
|
+
gwp_per_category_declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_category_declared_unit)).group())
|
197
|
+
gwp_per_unit_volume = (gwp_per_category_declared_unit_value / 1000) * density
|
198
|
+
gwp_per_unit_volume = round(gwp_per_unit_volume,2)
|
199
|
+
|
200
|
+
# Per area
|
201
|
+
if declared_unit and "sf" in declared_unit:
|
202
|
+
gwp_per_category_declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_category_declared_unit)).group())
|
203
|
+
declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(declared_unit)).group())
|
204
|
+
gwp_per_unit_area = (gwp_per_category_declared_unit_value/declared_unit_value)/0.092903
|
205
|
+
gwp_per_unit_area = round(gwp_per_unit_area,2)
|
206
|
+
|
207
|
+
igu_object["gwp_per_unit_volume"] = f"{gwp_per_unit_volume} kg CO2 e/m3"
|
208
|
+
|
209
|
+
if declared_unit and "m2" in declared_unit:
|
210
|
+
gwp_per_category_declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_category_declared_unit)).group())
|
211
|
+
igu_object["gwp_per_unit_area"] = f"{gwp_per_category_declared_unit_value} kg CO2 e/m2"
|
212
|
+
else:
|
213
|
+
igu_object["gwp_per_unit_area"] = f"{gwp_per_unit_area} kg CO2 e/m2"
|
214
|
+
|
215
|
+
if gwp_per_unit_mass:
|
216
|
+
gwp_per_unit_mass = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_unit_mass)).group())
|
217
|
+
gwp_per_unit_mass = round(gwp_per_unit_mass,2)
|
218
|
+
igu_object["gwp_per_unit_mass"] = f"{gwp_per_unit_mass} kg CO2 e/kg"
|
219
|
+
else:
|
220
|
+
igu_object["gwp_per_unit_mass"] = "Not avaialble in this EPD"
|
221
|
+
object_key = "object" + str(igu_epd_no)
|
222
|
+
igu_epd_data[object_key] = igu_object
|
223
|
+
igu_gwp_per_volume.append(gwp_per_unit_volume)
|
224
|
+
igu_gwp_per_area.append(gwp_per_unit_area)
|
225
|
+
igu_gwp_per_mass.append(gwp_per_unit_mass)
|
226
|
+
pprint.pp(igu_object)
|
227
|
+
#print all gwp per volume
|
228
|
+
igu_gwp_per_volume = [float(item) for item in igu_gwp_per_volume if isinstance(item, (int, float)) or item.replace('.', '', 1).isdigit()]
|
229
|
+
# Calculate the mean
|
230
|
+
if igu_gwp_per_volume: # Check if the list is not empty
|
231
|
+
mean_value = statistics.mean(igu_gwp_per_volume)
|
232
|
+
print("Mean of GWP per volume:", mean_value)
|
233
|
+
else:
|
234
|
+
print("No numeric values to calculate mean.")
|
235
|
+
print(igu_gwp_per_volume)
|
236
|
+
|
237
|
+
for wframe_epd_no, wframe_epd in enumerate(wframe_response, start=1):
|
238
|
+
print(f"========================================EPD No. {wframe_epd_no}:==========================================")
|
239
|
+
# Initialize gwp per unit volume of window frame
|
240
|
+
gwp_per_unit_volume = "Not specified"
|
241
|
+
gwp_per_unit_area = "Not specified"
|
242
|
+
gwp_per_unit_mass = "Not specified"
|
243
|
+
# Create an empty list to store each key,value pair in an individual EPD object
|
244
|
+
wframe_object = {}
|
245
|
+
|
246
|
+
for key, value in wframe_epd.items():
|
247
|
+
if value is not None and key not in exclude_keys:
|
248
|
+
# Append the key-value pair to the list
|
249
|
+
wframe_object[key] = value
|
250
|
+
# Extract the relevant keys
|
251
|
+
declared_unit = wframe_epd.get("declared_unit")
|
252
|
+
thickness_unit = wframe_epd.get("thickness")
|
253
|
+
gwp_per_category_declared_unit = wframe_epd.get("gwp_per_category_declared_unit")
|
254
|
+
warnings = wframe_epd.get("warnings")
|
255
|
+
mass_per_declared_unit = wframe_epd.get("mass_per_declared_unit")
|
256
|
+
density_unit = wframe_epd.get("density")
|
257
|
+
gwp_per_unit_mass = wframe_epd.get("gwp_per_kg")
|
258
|
+
|
259
|
+
# Per volume
|
260
|
+
if declared_unit and "m3" in declared_unit:
|
261
|
+
gwp_per_category_declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_category_declared_unit)).group())
|
262
|
+
declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(declared_unit)).group())
|
263
|
+
gwp_per_unit_volume = gwp_per_category_declared_unit_value/declared_unit_value
|
264
|
+
|
265
|
+
elif declared_unit and "m2" in declared_unit and thickness_unit and "mm" in str(thickness_unit):
|
266
|
+
thickness = float(re.search(r"[-+]?\d*\.?\d+", thickness_unit).group()) # Extract numeric part
|
267
|
+
gwp_per_category_declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_category_declared_unit)).group())
|
268
|
+
thickness = thickness * 1e-3 # Convert thickness to meters
|
269
|
+
gwp_per_unit_volume = round(gwp_per_category_declared_unit_value / thickness,2)
|
270
|
+
|
271
|
+
elif density_unit and gwp_per_unit_mass:
|
272
|
+
density = float(re.search(r"[-+]?\d*\.?\d+", density_unit).group()) # Extract numeric part
|
273
|
+
gwp_per_unit_mass = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_unit_mass)).group())
|
274
|
+
gwp_per_unit_volume = gwp_per_unit_mass*density
|
275
|
+
gwp_per_unit_volume = round(gwp_per_unit_volume,2)
|
276
|
+
|
277
|
+
elif declared_unit and "t" in declared_unit and density_unit:
|
278
|
+
density = float(re.search(r"[-+]?\d*\.?\d+", density_unit).group()) # Extract numeric part
|
279
|
+
gwp_per_category_declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_category_declared_unit)).group())
|
280
|
+
gwp_per_unit_volume = (gwp_per_category_declared_unit_value / 1000) * density
|
281
|
+
gwp_per_unit_volume = round(gwp_per_unit_volume,2)
|
282
|
+
|
283
|
+
# Per area
|
284
|
+
if declared_unit and "sf" in declared_unit:
|
285
|
+
gwp_per_category_declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_category_declared_unit)).group())
|
286
|
+
declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(declared_unit)).group())
|
287
|
+
gwp_per_unit_area = (gwp_per_category_declared_unit_value/declared_unit_value)/0.092903
|
288
|
+
gwp_per_unit_area = round(gwp_per_unit_area,2)
|
289
|
+
|
290
|
+
wframe_object["gwp_per_unit_volume"] = f"{gwp_per_unit_volume} kg CO2 e/m3"
|
291
|
+
|
292
|
+
if declared_unit and "m2" in declared_unit:
|
293
|
+
gwp_per_category_declared_unit_value = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_category_declared_unit)).group())
|
294
|
+
wframe_object["gwp_per_unit_area"] = f"{gwp_per_category_declared_unit_value} kg CO2 e/m2"
|
295
|
+
else:
|
296
|
+
wframe_object["gwp_per_unit_area"] = f"{gwp_per_unit_area} kg CO2 e/m2"
|
297
|
+
|
298
|
+
if gwp_per_unit_mass:
|
299
|
+
gwp_per_unit_mass = float(re.search(r"[-+]?\d*\.?\d+", str(gwp_per_unit_mass)).group())
|
300
|
+
gwp_per_unit_mass = round(gwp_per_unit_mass,2)
|
301
|
+
wframe_object["gwp_per_unit_mass"] = f"{gwp_per_unit_mass} kg CO2 e/kg"
|
302
|
+
else:
|
303
|
+
wframe_object["gwp_per_unit_mass"] = "Not avaialble in this EPD"
|
304
|
+
object_key = "object" + str(wframe_epd_no)
|
305
|
+
wframe_epd_data[object_key] = wframe_object
|
306
|
+
wframe_gwp_per_volume.append(gwp_per_unit_volume)
|
307
|
+
wframe_gwp_per_area.append(gwp_per_unit_area)
|
308
|
+
wframe_gwp_per_mass.append(gwp_per_unit_mass)
|
309
|
+
pprint.pp(wframe_object)
|
310
|
+
|
311
|
+
#print all gwp per volume
|
312
|
+
wframe_gwp_per_volume = [float(item) for item in wframe_gwp_per_volume if isinstance(item, (int, float)) or item.replace('.', '', 1).isdigit()]
|
313
|
+
# Calculate the mean
|
314
|
+
if wframe_gwp_per_volume: # Check if the list is not empty
|
315
|
+
mean_value = statistics.mean(wframe_gwp_per_volume)
|
316
|
+
min_value = min(wframe_gwp_per_volume)
|
317
|
+
max_value = max(wframe_gwp_per_volume)
|
318
|
+
print("Mean of GWP per m3:", mean_value)
|
319
|
+
print("Minimum GWP per m3:", min_value)
|
320
|
+
print("Maximum GWP per m3:", max_value)
|
321
|
+
else:
|
322
|
+
print("No numeric values to calculate statistical parameters.")
|
Binary file
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# save this function for wall construction
|
2
|
+
def calculate_geometry(self, sub_surface):
|
3
|
+
"""
|
4
|
+
Calculate the length, width, perimeter, and area of the window from its vertices.
|
5
|
+
Assumes the window is a quadrilateral (typically a rectangle).
|
6
|
+
"""
|
7
|
+
vertices = sub_surface.vertices()
|
8
|
+
if len(vertices) != 4:
|
9
|
+
return {
|
10
|
+
"length": 0.0,
|
11
|
+
"width": 0.0,
|
12
|
+
"perimeter": 0.0,
|
13
|
+
"area": 0.0
|
14
|
+
}
|
15
|
+
|
16
|
+
# Calculate all edge lengths
|
17
|
+
edge_lengths = []
|
18
|
+
perimeter = 0.0
|
19
|
+
for i in range(4):
|
20
|
+
v1 = vertices[i]
|
21
|
+
v2 = vertices[(i + 1) % 4]
|
22
|
+
edge = v2 - v1
|
23
|
+
length = edge.length()
|
24
|
+
edge_lengths.append(length)
|
25
|
+
perimeter += length
|
26
|
+
|
27
|
+
# Assume opposite edges are equal, so we can group into two unique lengths
|
28
|
+
length = max(edge_lengths)
|
29
|
+
width = min(edge_lengths)
|
30
|
+
|
31
|
+
# Area = length × width
|
32
|
+
area = length * width
|
33
|
+
|
34
|
+
return {
|
35
|
+
"length": length,
|
36
|
+
"width": width,
|
37
|
+
"perimeter": perimeter,
|
38
|
+
"area": area
|
39
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
**************API_TOKEN*************
|
2
|
+
"5fk7wP4cJg6pcmx6ncZN0ftMdoVR8u"
|
3
|
+
**************IGU_URL*************
|
4
|
+
"https://api.buildingtransparency.org/api/materials?page_number=1&page_size=100&mf=!EC3%20search(%22InsulatingGlazingUnits%22)%20WHERE%20%0A%20%20jurisdiction%3A%20IN(%22021%22)%20AND%0A%20%20epd__date_validity_ends%3A%20%3E%20%222024-12-05%22%20AND%0A%20%20epd_types%3A%20IN(%22Product%20EPDs%22)%20!pragma%20eMF(%222.0%2F1%22)%2C%20lcia(%22TRACI%202.1%22)"
|
5
|
+
**************WFRAME_URL*************
|
6
|
+
"aaa"
|
7
|
+
**********************HEADERS**************************
|
8
|
+
{'Accept': 'application/json', 'Authorization': 'Bearer 5fk7wP4cJg6pcmx6ncZN0ftMdoVR8u'}
|
9
|
+
test_number_of_arguments_and_argument_names() method level...
|
10
|
+
test_good_argument_values() method level...
|
11
|
+
user_arguments: igu_component_name (IGU Component Name)
|
12
|
+
String, Required
|
13
|
+
Value:
|
14
|
+
|
15
|
+
user_arguments: frame_cross_section_area (Frame Cross Section Area (m�))
|
16
|
+
Double, Required
|
17
|
+
Value:
|
18
|
+
|
19
|
+
user_arguments: frame_perimeter_length (Frame Perimeter Length (m))
|
20
|
+
Double, Required
|
21
|
+
Value:
|
22
|
+
|
23
|
+
user_arguments: declared_unit (Declared Unit for GWP)
|
24
|
+
String, Required
|
25
|
+
Value:
|
26
|
+
|
27
|
+
user_arguments: gwp (Global Warming Potential (GWP) Value)
|
28
|
+
Double, Required
|
29
|
+
Value:
|
30
|
+
|
31
|
+
user_argument: declared_unit = m2
|
32
|
+
user_argument: frame_cross_section_area = 0.02
|
33
|
+
user_argument: frame_perimeter_length = 10
|
34
|
+
user_argument: gwp = 0
|
35
|
+
user_argument: igu_component_name = TestIGU
|
36
|
+
Measure result: Success
|
37
|
+
Modified model saved to: C:\All_repos\openstudio-ee-gem\lib\measures\window_enhancement\tests\output\example_model_with_enhancements.osm
|
38
|
+
Running test_measure_changes_building()...
|
39
|
+
Measure success: Fail
|
data/openstudio-ee.gemspec
CHANGED
@@ -12,10 +12,11 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.summary = 'Library and measures for OpenStudio for energy efficiency use cases'
|
13
13
|
spec.description = 'Library and measures for OpenStudio for energy efficiency use cases'
|
14
14
|
spec.metadata = {
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
'bug_tracker_uri' => 'https://github.com/NREL/openstudio-ee-gem/issues',
|
16
|
+
'changelog_uri' => 'https://github.com/NREL/openstudio-ee-gem/blob/develop/CHANGELOG.md',
|
17
|
+
# 'documentation_uri' => 'https://www.rubydoc.info/gems/openstudio-ee-gem/#{gem.version}',
|
18
|
+
'source_code_uri' => "https://github.com/NREL/openstudio-ee-gem/tree/v#{spec.version}",
|
19
|
+
'rubygems_mfa_required' => 'true'
|
19
20
|
}
|
20
21
|
|
21
22
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
@@ -28,15 +29,16 @@ Gem::Specification.new do |spec|
|
|
28
29
|
spec.required_ruby_version = '~> 3.2.2'
|
29
30
|
|
30
31
|
spec.add_dependency 'bundler', '~> 2.4.10'
|
32
|
+
spec.add_dependency 'multipart-post', '2.4.0'
|
31
33
|
spec.add_dependency 'openstudio-extension', '~> 0.9.3'
|
32
34
|
spec.add_dependency 'openstudio-standards', '0.8.2'
|
33
|
-
spec.add_dependency 'multipart-post', '2.4.0'
|
34
35
|
|
36
|
+
spec.add_development_dependency 'octokit', '4.18.0' # for change logs
|
37
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
38
|
+
spec.add_development_dependency 'rspec', '~> 3.9'
|
35
39
|
spec.add_development_dependency 'rubocop', '1.50'
|
36
40
|
spec.add_development_dependency 'rubocop-checkstyle_formatter', '0.6.0'
|
37
41
|
spec.add_development_dependency 'rubocop-performance', '1.20.0'
|
42
|
+
spec.add_development_dependency 'rubocop-rake', '~> 0.6.0'
|
38
43
|
spec.add_development_dependency 'simplecov', '0.22.0'
|
39
|
-
spec.add_development_dependency 'rake', '~> 13.0'
|
40
|
-
spec.add_development_dependency 'rspec', '~> 3.9'
|
41
|
-
spec.add_development_dependency 'octokit', '4.18.0' # for change logs
|
42
44
|
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Local workflow testing script
|
4
|
+
# This script tests the key components of the GitHub Actions workflow locally
|
5
|
+
|
6
|
+
set -e
|
7
|
+
|
8
|
+
echo "🔍 Testing OpenStudio EE Gem Workflow Components Locally"
|
9
|
+
echo "========================================================"
|
10
|
+
|
11
|
+
# Test 1: Check Docker image availability
|
12
|
+
echo "1. Testing Docker image availability..."
|
13
|
+
|
14
|
+
# Check if images are already available locally
|
15
|
+
if docker images --format "table {{.Repository}}:{{.Tag}}" | grep -q "nrel/openstudio:develop"; then
|
16
|
+
echo "✅ nrel/openstudio:develop image found locally"
|
17
|
+
DOCKER_IMAGE="nrel/openstudio:develop"
|
18
|
+
elif docker images --format "table {{.Repository}}:{{.Tag}}" | grep -q "nrel/openstudio:3.10.0"; then
|
19
|
+
echo "✅ nrel/openstudio:3.10.0 image found locally"
|
20
|
+
DOCKER_IMAGE="nrel/openstudio:3.10.0"
|
21
|
+
else
|
22
|
+
echo "⚠️ No local OpenStudio Docker images found. Attempting to pull..."
|
23
|
+
|
24
|
+
# Try to pull the develop image
|
25
|
+
if docker pull nrel/openstudio:develop 2>/dev/null; then
|
26
|
+
echo "✅ Successfully pulled nrel/openstudio:develop image"
|
27
|
+
DOCKER_IMAGE="nrel/openstudio:develop"
|
28
|
+
else
|
29
|
+
echo "❌ Failed to pull nrel/openstudio:develop image"
|
30
|
+
echo " This might be due to:"
|
31
|
+
echo " - Repository requires authentication (docker login)"
|
32
|
+
echo " - Network connectivity issues"
|
33
|
+
echo " - Repository access restrictions"
|
34
|
+
|
35
|
+
# Try fallback to 3.10.0
|
36
|
+
echo " Trying fallback to nrel/openstudio:3.10.0..."
|
37
|
+
if docker pull nrel/openstudio:3.10.0 2>/dev/null; then
|
38
|
+
echo "✅ Successfully pulled nrel/openstudio:3.10.0 image"
|
39
|
+
DOCKER_IMAGE="nrel/openstudio:3.10.0"
|
40
|
+
else
|
41
|
+
echo "❌ Failed to pull any OpenStudio Docker image"
|
42
|
+
echo ""
|
43
|
+
echo "🔧 To fix this issue:"
|
44
|
+
echo " 1. Check if you need to run 'docker login' for private registries"
|
45
|
+
echo " 2. Verify network connectivity to Docker Hub"
|
46
|
+
echo " 3. Contact your system administrator about Docker registry access"
|
47
|
+
echo ""
|
48
|
+
echo "⏭️ Skipping Docker-based tests. The GitHub Actions workflow will"
|
49
|
+
echo " run in the GitHub environment which should have proper access."
|
50
|
+
exit 0
|
51
|
+
fi
|
52
|
+
fi
|
53
|
+
fi
|
54
|
+
|
55
|
+
DOCKER_IMAGE="${DOCKER_IMAGE:-nrel/openstudio:develop}"
|
56
|
+
echo "Using Docker image: $DOCKER_IMAGE"
|
57
|
+
|
58
|
+
# Test 2: Run basic container commands
|
59
|
+
echo ""
|
60
|
+
echo "2. Testing basic container functionality..."
|
61
|
+
docker run --rm -v "$(pwd):/workspace" -w /workspace "$DOCKER_IMAGE" bash -c "
|
62
|
+
echo 'Container started successfully'
|
63
|
+
echo 'Ruby Version:' \$(ruby -v)
|
64
|
+
echo 'Bundle Version:' \$(bundle -v)
|
65
|
+
echo 'OpenStudio Version:' \$(openstudio openstudio_version)
|
66
|
+
echo 'Listing OpenStudio Gems:' \$(openstudio gem_list)
|
67
|
+
"
|
68
|
+
|
69
|
+
# Test 3: Test locale setup
|
70
|
+
echo ""
|
71
|
+
echo "3. Testing locale configuration..."
|
72
|
+
docker run --rm -u root "$DOCKER_IMAGE" bash -c "
|
73
|
+
apt-get update -qq
|
74
|
+
apt-get install -y locales
|
75
|
+
locale-gen en_US.UTF-8
|
76
|
+
echo 'Locale setup completed'
|
77
|
+
locale -a | grep en_US
|
78
|
+
"
|
79
|
+
|
80
|
+
# Test 4: Test bundle install
|
81
|
+
echo ""
|
82
|
+
echo "4. Testing bundle install..."
|
83
|
+
docker run --rm -v "$(pwd):/workspace" -w /workspace "$DOCKER_IMAGE" bash -c "
|
84
|
+
if bundle install; then
|
85
|
+
echo '✅ Bundle install successful'
|
86
|
+
bundle list
|
87
|
+
else
|
88
|
+
echo '❌ Bundle install failed'
|
89
|
+
exit 1
|
90
|
+
fi
|
91
|
+
"
|
92
|
+
|
93
|
+
# Test 5: Test basic rake tasks (if available)
|
94
|
+
echo ""
|
95
|
+
echo "5. Testing rake tasks availability..."
|
96
|
+
docker run --rm -v "$(pwd):/workspace" -w /workspace "$DOCKER_IMAGE" bash -c "
|
97
|
+
bundle install
|
98
|
+
echo 'Available rake tasks:'
|
99
|
+
bundle exec rake -T | head -10
|
100
|
+
"
|
101
|
+
|
102
|
+
# Test 6: Local environment validation (fallback if Docker fails)
|
103
|
+
if [ "$DOCKER_IMAGE" = "" ]; then
|
104
|
+
echo ""
|
105
|
+
echo "6. Testing local Ruby environment (Docker unavailable)..."
|
106
|
+
|
107
|
+
# Check Ruby version
|
108
|
+
if command -v ruby >/dev/null 2>&1; then
|
109
|
+
echo "Ruby Version: $(ruby -v)"
|
110
|
+
else
|
111
|
+
echo "❌ Ruby not found in local environment"
|
112
|
+
fi
|
113
|
+
|
114
|
+
# Check Bundle version
|
115
|
+
if command -v bundle >/dev/null 2>&1; then
|
116
|
+
echo "Bundle Version: $(bundle -v)"
|
117
|
+
|
118
|
+
# Test bundle install
|
119
|
+
if bundle check >/dev/null 2>&1 || bundle install; then
|
120
|
+
echo "✅ Bundle dependencies satisfied"
|
121
|
+
|
122
|
+
# List available rake tasks
|
123
|
+
echo "Available rake tasks:"
|
124
|
+
bundle exec rake -T | head -5
|
125
|
+
else
|
126
|
+
echo "❌ Bundle install failed"
|
127
|
+
fi
|
128
|
+
else
|
129
|
+
echo "❌ Bundler not found in local environment"
|
130
|
+
fi
|
131
|
+
fi
|
132
|
+
|
133
|
+
echo ""
|
134
|
+
echo "🎉 Local workflow testing completed!"
|
135
|
+
echo ""
|
136
|
+
echo "📝 Notes:"
|
137
|
+
echo " - Docker image testing completed (using $DOCKER_IMAGE)"
|
138
|
+
echo " - All basic commands work as expected"
|
139
|
+
echo " - Bundle install succeeds"
|
140
|
+
echo " - Ready for GitHub Actions testing"
|
141
|
+
echo ""
|
142
|
+
echo "⚠️ To test the full workflow:"
|
143
|
+
echo " 1. Push changes to a test branch"
|
144
|
+
echo " 2. Monitor the GitHub Actions run"
|
145
|
+
echo " 3. Check the S3 bucket for uploaded results"
|
146
|
+
echo ""
|
147
|
+
echo "🔍 Alternative local testing (without Docker):"
|
148
|
+
echo " If Docker testing failed, you can still validate the workflow logic:"
|
149
|
+
echo " 1. Check that bundle install works: bundle install"
|
150
|
+
echo " 2. List available rake tasks: bundle exec rake -T"
|
151
|
+
echo " 3. Test individual rake tasks if available"
|
152
|
+
echo " 4. The GitHub Actions environment should have proper Docker access"
|