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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.coverage +0 -0
  3. data/.github/workflows/test-with-openstudio.yml +109 -74
  4. data/.gitignore +21 -0
  5. data/.rubocop.yml +15 -2
  6. data/CHANGELOG.md +6 -0
  7. data/Gemfile +7 -8
  8. data/README.md +3 -2
  9. data/WORKFLOW_CHANGES.md +74 -0
  10. data/lib/measures/AddDaylightSensors/measure.rb +79 -79
  11. data/lib/measures/AddDaylightSensors/measure.xml +4 -4
  12. data/lib/measures/AddOverhangsByProjectionFactor/measure.rb +38 -41
  13. data/lib/measures/AddOverhangsByProjectionFactor/measure.xml +4 -4
  14. data/lib/measures/EnableDemandControlledVentilation/measure.rb +37 -40
  15. data/lib/measures/EnableDemandControlledVentilation/measure.xml +4 -4
  16. data/lib/measures/EnableEconomizerControl/measure.rb +36 -37
  17. data/lib/measures/EnableEconomizerControl/measure.xml +4 -4
  18. data/lib/measures/GLHEProExportLoadsforGroundHeatExchangerSizing/measure.rb +27 -41
  19. data/lib/measures/GLHEProExportLoadsforGroundHeatExchangerSizing/measure.xml +4 -4
  20. data/lib/measures/GLHEProGFunctionImport/measure.rb +11 -15
  21. data/lib/measures/GLHEProGFunctionImport/measure.xml +4 -4
  22. data/lib/measures/GLHEProSetupExportLoadsforGroundHeatExchangerSizing/measure.rb +5 -9
  23. data/lib/measures/GLHEProSetupExportLoadsforGroundHeatExchangerSizing/measure.xml +3 -3
  24. data/lib/measures/ImproveFanBeltEfficiency/measure.rb +78 -95
  25. data/lib/measures/ImproveFanBeltEfficiency/measure.xml +6 -6
  26. data/lib/measures/ImproveMotorEfficiency/measure.rb +75 -100
  27. data/lib/measures/ImproveMotorEfficiency/measure.xml +6 -6
  28. data/lib/measures/IncreaseInsulationRValueForExteriorWalls/measure.rb +137 -130
  29. data/lib/measures/IncreaseInsulationRValueForExteriorWalls/measure.xml +4 -4
  30. data/lib/measures/IncreaseInsulationRValueForExteriorWallsByPercentage/measure.rb +114 -115
  31. data/lib/measures/IncreaseInsulationRValueForExteriorWallsByPercentage/measure.xml +3 -3
  32. data/lib/measures/IncreaseInsulationRValueForRoofs/measure.rb +137 -130
  33. data/lib/measures/IncreaseInsulationRValueForRoofs/measure.xml +4 -4
  34. data/lib/measures/IncreaseInsulationRValueForRoofsByPercentage/measure.rb +114 -115
  35. data/lib/measures/IncreaseInsulationRValueForRoofsByPercentage/measure.xml +3 -3
  36. data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/measure.rb +69 -63
  37. data/lib/measures/ReduceElectricEquipmentLoadsByPercentage/measure.xml +6 -6
  38. data/lib/measures/ReduceLightingLoadsByPercentage/measure.rb +77 -66
  39. data/lib/measures/ReduceLightingLoadsByPercentage/measure.xml +6 -6
  40. data/lib/measures/ReduceNightTimeElectricEquipmentLoads/measure.rb +45 -43
  41. data/lib/measures/ReduceNightTimeElectricEquipmentLoads/measure.xml +4 -4
  42. data/lib/measures/ReduceNightTimeLightingLoads/measure.rb +45 -43
  43. data/lib/measures/ReduceNightTimeLightingLoads/measure.xml +4 -4
  44. data/lib/measures/ReduceSpaceInfiltrationByPercentage/measure.rb +58 -52
  45. data/lib/measures/ReduceSpaceInfiltrationByPercentage/measure.xml +6 -6
  46. data/lib/measures/ReduceVentilationByPercentage/measure.rb +49 -46
  47. data/lib/measures/ReduceVentilationByPercentage/measure.xml +6 -6
  48. data/lib/measures/add_variable_speed_rtu_control_logic/measure.rb +31 -23
  49. data/lib/measures/add_variable_speed_rtu_control_logic/measure.xml +4 -4
  50. data/lib/measures/create_variable_speed_rtu/measure.rb +166 -174
  51. data/lib/measures/create_variable_speed_rtu/measure.xml +6 -6
  52. data/lib/measures/fan_assist_night_ventilation/measure.rb +33 -32
  53. data/lib/measures/fan_assist_night_ventilation/measure.xml +4 -4
  54. data/lib/measures/nze_hvac/measure.rb +72 -62
  55. data/lib/measures/nze_hvac/measure.xml +4 -4
  56. data/lib/measures/replace_water_heater_mixed_with_thermal_storage_chilled_water/measure.rb +16 -19
  57. data/lib/measures/replace_water_heater_mixed_with_thermal_storage_chilled_water/measure.xml +4 -4
  58. data/lib/measures/window_enhancement/LICENSE.md +14 -0
  59. data/lib/measures/window_enhancement/README.md +112 -0
  60. data/lib/measures/window_enhancement/docs/.gitkeep +0 -0
  61. data/lib/measures/window_enhancement/measure.py +386 -0
  62. data/lib/measures/window_enhancement/measure.xml +271 -0
  63. data/lib/measures/window_enhancement/resources/EC3_lookup.py +321 -0
  64. data/lib/measures/window_enhancement/resources/Test_API.py +32 -0
  65. data/lib/measures/window_enhancement/resources/__pycache__/EC3_lookup.cpython-39.pyc +0 -0
  66. data/lib/measures/window_enhancement/resources/__pycache__/Original_EC3_lookup.py +322 -0
  67. data/lib/measures/window_enhancement/resources/__pycache__/Test_API.cpython-39.pyc +0 -0
  68. data/lib/measures/window_enhancement/resources/calculate_perimeter.py +39 -0
  69. data/lib/measures/window_enhancement/test_output.log +39 -0
  70. data/lib/openstudio/ee_measures/version.rb +1 -1
  71. data/openstudio-ee.gemspec +10 -8
  72. data/test-workflow-locally.sh +152 -0
  73. metadata +64 -35
  74. 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.")
@@ -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
@@ -7,6 +7,6 @@
7
7
 
8
8
  module OpenStudio
9
9
  module EeMeasures
10
- VERSION = '0.12.3'
10
+ VERSION = '0.12.5'
11
11
  end
12
12
  end
@@ -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
- '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}"
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"