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
@@ -9,17 +9,17 @@
|
|
9
9
|
class AddDaylightSensors < OpenStudio::Measure::ModelMeasure
|
10
10
|
# define the name that a user will see
|
11
11
|
def name
|
12
|
-
|
12
|
+
'Add Daylight Sensor at the Center of Spaces with a Specified Space Type Assigned'
|
13
13
|
end
|
14
14
|
|
15
15
|
# human readable description
|
16
16
|
def description
|
17
|
-
|
17
|
+
'This measure will add daylighting controls to spaces that that have space types assigned with names containing the string in the argument. You can also add a cost per space for sensors added to the model.'
|
18
18
|
end
|
19
19
|
|
20
20
|
# human readable description of modeling approach
|
21
21
|
def modeler_description
|
22
|
-
|
22
|
+
"Make an array of the spaces that meet the criteria. Locate the sensor x and y values by averaging the min and max X and Y values from floor surfaces in the space. If a space already has a daylighting control, do not add a new one and leave the original in place. Warn the user if the space isn't assigned to a thermal zone, or if the space doesn't have any translucent surfaces. Note that the cost is added to the space not the sensor. If the sensor is removed at a later date, the cost will remain."
|
23
23
|
end
|
24
24
|
|
25
25
|
# define the arguments that the user will input
|
@@ -40,14 +40,15 @@ class AddDaylightSensors < OpenStudio::Measure::ModelMeasure
|
|
40
40
|
# looping through sorted hash of model objects
|
41
41
|
space_type_args_hash.sort.map do |key, value|
|
42
42
|
# only include if space type is used in the model
|
43
|
-
|
43
|
+
unless value.spaces.empty?
|
44
44
|
space_type_handles << value.handle.to_s
|
45
45
|
space_type_display_names << key
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
49
|
# make a choice argument for space type
|
50
|
-
space_type = OpenStudio::Measure::OSArgument.makeChoiceArgument('space_type', space_type_handles,
|
50
|
+
space_type = OpenStudio::Measure::OSArgument.makeChoiceArgument('space_type', space_type_handles,
|
51
|
+
space_type_display_names, true)
|
51
52
|
space_type.setDisplayName('Add Daylight Sensors to Spaces of This Space Type')
|
52
53
|
args << space_type
|
53
54
|
|
@@ -144,7 +145,7 @@ class AddDaylightSensors < OpenStudio::Measure::ModelMeasure
|
|
144
145
|
om_frequency.setDefaultValue(1)
|
145
146
|
args << om_frequency
|
146
147
|
|
147
|
-
|
148
|
+
args
|
148
149
|
end
|
149
150
|
|
150
151
|
# define what happens when the measure is run
|
@@ -152,9 +153,7 @@ class AddDaylightSensors < OpenStudio::Measure::ModelMeasure
|
|
152
153
|
super(model, runner, user_arguments)
|
153
154
|
|
154
155
|
# use the built-in error checking
|
155
|
-
|
156
|
-
return false
|
157
|
-
end
|
156
|
+
return false unless runner.validateUserArguments(arguments(model), user_arguments)
|
158
157
|
|
159
158
|
# assign the user inputs to variables
|
160
159
|
space_type = runner.getOptionalWorkspaceObjectChoiceValue('space_type', user_arguments, model)
|
@@ -181,13 +180,11 @@ class AddDaylightSensors < OpenStudio::Measure::ModelMeasure
|
|
181
180
|
runner.registerError("The selected space type with handle '#{handle}' was not found in the model. It may have been removed by another measure.")
|
182
181
|
end
|
183
182
|
return false
|
183
|
+
elsif !space_type.get.to_SpaceType.empty?
|
184
|
+
space_type = space_type.get.to_SpaceType.get
|
184
185
|
else
|
185
|
-
|
186
|
-
|
187
|
-
else
|
188
|
-
runner.registerError('Script Error - argument not showing up as space type.')
|
189
|
-
return false
|
190
|
-
end
|
186
|
+
runner.registerError('Script Error - argument not showing up as space type.')
|
187
|
+
return false
|
191
188
|
end
|
192
189
|
|
193
190
|
# check the setpoint for reasonableness
|
@@ -238,17 +235,15 @@ class AddDaylightSensors < OpenStudio::Measure::ModelMeasure
|
|
238
235
|
if (expected_life < 1) && (expected_life > 100)
|
239
236
|
runner.registerError('Choose an integer greater than 0 and less than or equal to 100 for Expected Life.')
|
240
237
|
end
|
241
|
-
if om_frequency < 1
|
242
|
-
runner.registerError('Choose an integer greater than 0 for O & M Frequency.')
|
243
|
-
end
|
238
|
+
runner.registerError('Choose an integer greater than 0 for O & M Frequency.') if om_frequency < 1
|
244
239
|
|
245
240
|
# short def to make numbers pretty (converts 4125001.25641 to 4,125,001.26 or 4,125,001). The definition be called through this measure
|
246
241
|
def neat_numbers(number, roundto = 2) # round to 0 or 2)
|
247
|
-
if roundto == 2
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
242
|
+
number = if roundto == 2
|
243
|
+
format '%.2f', number
|
244
|
+
else
|
245
|
+
number.round
|
246
|
+
end
|
252
247
|
# regex to add commas
|
253
248
|
number.to_s.reverse.gsub(/([0-9]{3}(?=([0-9])))/, '\\1,').reverse
|
254
249
|
end
|
@@ -259,14 +254,12 @@ class AddDaylightSensors < OpenStudio::Measure::ModelMeasure
|
|
259
254
|
objects.each do |object|
|
260
255
|
object_LCCs = object.lifeCycleCosts
|
261
256
|
object_LCCs.each do |object_LCC|
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
end
|
266
|
-
end
|
257
|
+
next unless (object_LCC.category == 'Construction') || (object_LCC.category == 'Salvage')
|
258
|
+
|
259
|
+
counter += object_LCC.totalCost if object_LCC.yearsFromStart == 0
|
267
260
|
end
|
268
261
|
end
|
269
|
-
|
262
|
+
counter
|
270
263
|
end
|
271
264
|
|
272
265
|
# unit conversion from IP units to SI units
|
@@ -297,15 +290,14 @@ class AddDaylightSensors < OpenStudio::Measure::ModelMeasure
|
|
297
290
|
spaces_using_space_type.each do |space_using_space_type|
|
298
291
|
if space_using_space_type.daylightingControls.empty?
|
299
292
|
space_zone = space_using_space_type.thermalZone
|
300
|
-
if
|
293
|
+
if space_zone.empty?
|
294
|
+
runner.registerWarning("Space '#{space_using_space_type.name}' is not associated with a thermal zone. It won't be part of the EnergyPlus simulation.")
|
295
|
+
else
|
301
296
|
space_zone = space_zone.get
|
302
297
|
if space_zone.primaryDaylightingControl.empty? && space_zone.secondaryDaylightingControl.empty?
|
303
298
|
spaces_using_space_type_in_zones_without_sensors << space_using_space_type
|
304
|
-
elsif
|
305
|
-
runner.registerWarning("Thermal zone '#{space_zone.name}' which includes space '#{space_using_space_type.name}' already had a daylighting sensor. No sensor was added to space '#{space_using_space_type.name}'.")
|
299
|
+
elsif runner.registerWarning("Thermal zone '#{space_zone.name}' which includes space '#{space_using_space_type.name}' already had a daylighting sensor. No sensor was added to space '#{space_using_space_type.name}'.")
|
306
300
|
end
|
307
|
-
else
|
308
|
-
runner.registerWarning("Space '#{space_using_space_type.name}' is not associated with a thermal zone. It won't be part of the EnergyPlus simulation.")
|
309
301
|
end
|
310
302
|
else
|
311
303
|
runner.registerWarning("Space '#{space_using_space_type.name}' already has a daylighting sensor. No sensor was added.")
|
@@ -323,9 +315,11 @@ class AddDaylightSensors < OpenStudio::Measure::ModelMeasure
|
|
323
315
|
has_ext_nat_light = false
|
324
316
|
space.surfaces.each do |surface|
|
325
317
|
next if surface.outsideBoundaryCondition != 'Outdoors'
|
318
|
+
|
326
319
|
surface.subSurfaces.each do |sub_surface|
|
327
320
|
next if sub_surface.subSurfaceType == 'Door'
|
328
321
|
next if sub_surface.subSurfaceType == 'OverheadDoor'
|
322
|
+
|
329
323
|
has_ext_nat_light = true
|
330
324
|
end
|
331
325
|
end
|
@@ -338,6 +332,7 @@ class AddDaylightSensors < OpenStudio::Measure::ModelMeasure
|
|
338
332
|
floors = []
|
339
333
|
space.surfaces.each do |surface|
|
340
334
|
next if surface.surfaceType != 'Floor'
|
335
|
+
|
341
336
|
floors << surface
|
342
337
|
end
|
343
338
|
|
@@ -373,16 +368,20 @@ class AddDaylightSensors < OpenStudio::Measure::ModelMeasure
|
|
373
368
|
starting_lcc_counter = space.lifeCycleCosts.size
|
374
369
|
|
375
370
|
# adding new cost items
|
376
|
-
lcc_mat = OpenStudio::Model::LifeCycleCost.createLifeCycleCost("LCC_Mat - #{sensor.name}", space,
|
371
|
+
lcc_mat = OpenStudio::Model::LifeCycleCost.createLifeCycleCost("LCC_Mat - #{sensor.name}", space,
|
372
|
+
material_cost, 'CostPerEach', 'Construction', expected_life, years_until_costs_start)
|
377
373
|
if demo_cost_initial_const
|
378
|
-
lcc_demo = OpenStudio::Model::LifeCycleCost.createLifeCycleCost("LCC_Demo - #{sensor.name}", space,
|
374
|
+
lcc_demo = OpenStudio::Model::LifeCycleCost.createLifeCycleCost("LCC_Demo - #{sensor.name}", space,
|
375
|
+
demolition_cost, 'CostPerEach', 'Salvage', expected_life, years_until_costs_start)
|
379
376
|
else
|
380
|
-
lcc_demo = OpenStudio::Model::LifeCycleCost.createLifeCycleCost("LCC_Demo - #{sensor.name}", space,
|
377
|
+
lcc_demo = OpenStudio::Model::LifeCycleCost.createLifeCycleCost("LCC_Demo - #{sensor.name}", space,
|
378
|
+
demolition_cost, 'CostPerEach', 'Salvage', expected_life, years_until_costs_start + expected_life)
|
381
379
|
end
|
382
|
-
lcc_om = OpenStudio::Model::LifeCycleCost.createLifeCycleCost("LCC_OM - #{sensor.name}", space, om_cost,
|
380
|
+
lcc_om = OpenStudio::Model::LifeCycleCost.createLifeCycleCost("LCC_OM - #{sensor.name}", space, om_cost,
|
381
|
+
'CostPerEach', 'Maintenance', om_frequency, 0)
|
383
382
|
|
384
383
|
if space.lifeCycleCosts.size - starting_lcc_counter == 3
|
385
|
-
|
384
|
+
unless warning_cost_assign_to_space
|
386
385
|
runner.registerInfo('Cost for daylight sensors was added to spaces. The cost will remain in the model unless the space is removed. Removing only the sensor will not remove the cost.')
|
387
386
|
warning_cost_assign_to_space = true
|
388
387
|
end
|
@@ -424,48 +423,47 @@ class AddDaylightSensors < OpenStudio::Measure::ModelMeasure
|
|
424
423
|
end
|
425
424
|
end
|
426
425
|
|
427
|
-
if
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
end
|
449
|
-
|
450
|
-
if primary_space
|
451
|
-
# setup primary sensor
|
452
|
-
sensor_primary = new_sensor_objects[primary_space.name.to_s]
|
453
|
-
zone.setPrimaryDaylightingControl(sensor_primary)
|
454
|
-
zone.setFractionofZoneControlledbyPrimaryDaylightingControl(fraction_zone_controlled * primary_area / (primary_area + secondary_area))
|
426
|
+
next if zone_spaces_with_new_sensors.empty?
|
427
|
+
|
428
|
+
# need to identify the two largest spaces
|
429
|
+
primary_area = 0
|
430
|
+
secondary_area = 0
|
431
|
+
primary_space = nil
|
432
|
+
secondary_space = nil
|
433
|
+
three_or_more_sensors = false
|
434
|
+
|
435
|
+
# dfg temp - need to add another if statement so only get spaces with sensors
|
436
|
+
zone_spaces_with_new_sensors.each do |zone_space|
|
437
|
+
zone_space_area = zone_space.floorArea
|
438
|
+
if zone_space_area > primary_area
|
439
|
+
primary_area = zone_space_area
|
440
|
+
primary_space = zone_space
|
441
|
+
elsif zone_space_area > secondary_area
|
442
|
+
secondary_area = zone_space_area
|
443
|
+
secondary_space = zone_space
|
444
|
+
else
|
445
|
+
# setup flag to warn user that more than 2 sensors can't be added to a space
|
446
|
+
three_or_more_sensors = true
|
455
447
|
end
|
448
|
+
end
|
456
449
|
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
450
|
+
if primary_space
|
451
|
+
# setup primary sensor
|
452
|
+
sensor_primary = new_sensor_objects[primary_space.name.to_s]
|
453
|
+
zone.setPrimaryDaylightingControl(sensor_primary)
|
454
|
+
zone.setFractionofZoneControlledbyPrimaryDaylightingControl(fraction_zone_controlled * primary_area / (primary_area + secondary_area))
|
455
|
+
end
|
463
456
|
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
457
|
+
if secondary_space
|
458
|
+
# setup secondary sensor
|
459
|
+
sensor_secondary = new_sensor_objects[secondary_space.name.to_s]
|
460
|
+
zone.setSecondaryDaylightingControl(sensor_secondary)
|
461
|
+
zone.setFractionofZoneControlledbySecondaryDaylightingControl(fraction_zone_controlled * secondary_area / (primary_area + secondary_area))
|
462
|
+
end
|
468
463
|
|
464
|
+
# warn that additional sensors were not used
|
465
|
+
if three_or_more_sensors == true
|
466
|
+
runner.registerWarning("Thermal zone '#{zone.name}' had more than two spaces with sensors. Only two sensors were associated with the thermal zone.")
|
469
467
|
end
|
470
468
|
end
|
471
469
|
|
@@ -483,9 +481,11 @@ class AddDaylightSensors < OpenStudio::Measure::ModelMeasure
|
|
483
481
|
yr0_capital_totalCosts = get_total_costs_for_objects(spaces_using_space_type)
|
484
482
|
|
485
483
|
# reporting final condition of model
|
486
|
-
runner.registerFinalCondition("Added daylighting controls to #{sensor_count} spaces, covering #{area_ip}. Initial year costs associated with the daylighting controls is $#{neat_numbers(
|
484
|
+
runner.registerFinalCondition("Added daylighting controls to #{sensor_count} spaces, covering #{area_ip}. Initial year costs associated with the daylighting controls is $#{neat_numbers(
|
485
|
+
yr0_capital_totalCosts, 0
|
486
|
+
)}.")
|
487
487
|
|
488
|
-
|
488
|
+
true
|
489
489
|
end
|
490
490
|
end
|
491
491
|
|
@@ -3,8 +3,8 @@
|
|
3
3
|
<schema_version>3.1</schema_version>
|
4
4
|
<name>add_daylight_sensors</name>
|
5
5
|
<uid>62babdc7-c81e-4dfd-96e8-af1628a55167</uid>
|
6
|
-
<version_id>
|
7
|
-
<version_modified>2025-
|
6
|
+
<version_id>39be2777-6303-4810-8fd0-aaae1dfa515b</version_id>
|
7
|
+
<version_modified>2025-09-25T15:33:42Z</version_modified>
|
8
8
|
<xml_checksum>0AC58119</xml_checksum>
|
9
9
|
<class_name>AddDaylightSensors</class_name>
|
10
10
|
<display_name>Add Daylight Sensor at the Center of Spaces with a Specified Space Type Assigned</display_name>
|
@@ -216,13 +216,13 @@
|
|
216
216
|
<filename>measure.rb</filename>
|
217
217
|
<filetype>rb</filetype>
|
218
218
|
<usage_type>script</usage_type>
|
219
|
-
<checksum>
|
219
|
+
<checksum>22008D9E</checksum>
|
220
220
|
</file>
|
221
221
|
<file>
|
222
222
|
<filename>AddDaylightSensors_Test.rb</filename>
|
223
223
|
<filetype>rb</filetype>
|
224
224
|
<usage_type>test</usage_type>
|
225
|
-
<checksum>
|
225
|
+
<checksum>2F7FEB8D</checksum>
|
226
226
|
</file>
|
227
227
|
<file>
|
228
228
|
<filename>ModelForDaylightSensors.osm</filename>
|
@@ -9,17 +9,17 @@
|
|
9
9
|
class AddOverhangsByProjectionFactor < OpenStudio::Measure::ModelMeasure
|
10
10
|
# define the name that a user will see
|
11
11
|
def name
|
12
|
-
|
12
|
+
'Add Overhangs by Projection Factor'
|
13
13
|
end
|
14
14
|
|
15
15
|
# human readable description
|
16
16
|
def description
|
17
|
-
|
17
|
+
'Add overhangs by projection factor to specified windows. The projection factor is the overhang depth divided by the window height. This can be applied to windows by the closest cardinal direction. If baseline model contains overhangs made by this measure, they will be replaced. Optionally the measure can delete any pre-existing space shading surfaces.'
|
18
18
|
end
|
19
19
|
|
20
20
|
# human readable description of modeling approach
|
21
21
|
def modeler_description
|
22
|
-
|
22
|
+
"If requested then delete existing space shading surfaces. Then loop through exterior windows. If the requested cardinal direction is the closest to the window, then add the overhang. Name the shading surface the same as the window but append with '-Overhang'. If a space shading surface of that name already exists, then delete it before making the new one. This measure has no life cycle cost arguments. You can see the economic impact of the measure by costing the construction used for the overhangs."
|
23
23
|
end
|
24
24
|
|
25
25
|
# define the arguments that the user will input
|
@@ -64,18 +64,19 @@ class AddOverhangsByProjectionFactor < OpenStudio::Measure::ModelMeasure
|
|
64
64
|
# looping through sorted hash of constructions
|
65
65
|
construction_args_hash.sort.map do |key, value|
|
66
66
|
# only include if construction is not used on surface
|
67
|
-
|
67
|
+
unless value.isFenestration
|
68
68
|
construction_handles << value.handle.to_s
|
69
69
|
construction_display_names << key
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
73
|
# make an argument for construction
|
74
|
-
construction = OpenStudio::Measure::OSArgument.makeChoiceArgument('construction', construction_handles,
|
74
|
+
construction = OpenStudio::Measure::OSArgument.makeChoiceArgument('construction', construction_handles,
|
75
|
+
construction_display_names, false)
|
75
76
|
construction.setDisplayName('Optionally Choose a Construction for the Overhangs')
|
76
77
|
args << construction
|
77
78
|
|
78
|
-
|
79
|
+
args
|
79
80
|
end
|
80
81
|
|
81
82
|
# define what happens when the measure is run
|
@@ -83,9 +84,7 @@ class AddOverhangsByProjectionFactor < OpenStudio::Measure::ModelMeasure
|
|
83
84
|
super(model, runner, user_arguments)
|
84
85
|
|
85
86
|
# use the built-in error checking
|
86
|
-
|
87
|
-
return false
|
88
|
-
end
|
87
|
+
return false unless runner.validateUserArguments(arguments(model), user_arguments)
|
89
88
|
|
90
89
|
# assign the user inputs to variables
|
91
90
|
projection_factor = runner.getDoubleArgumentValue('projection_factor', user_arguments)
|
@@ -117,29 +116,30 @@ class AddOverhangsByProjectionFactor < OpenStudio::Measure::ModelMeasure
|
|
117
116
|
return false
|
118
117
|
end
|
119
118
|
|
119
|
+
elsif !construction.get.to_Construction.empty?
|
120
|
+
construction = construction.get.to_Construction.get
|
120
121
|
else
|
121
|
-
|
122
|
-
|
123
|
-
else
|
124
|
-
runner.registerError('Script Error - argument not showing up as construction.')
|
125
|
-
return false
|
126
|
-
end
|
122
|
+
runner.registerError('Script Error - argument not showing up as construction.')
|
123
|
+
return false
|
127
124
|
end
|
128
125
|
|
129
126
|
# helper to make numbers pretty (converts 4125001.25641 to 4,125,001.26 or 4,125,001). The definition be called through this measure.
|
130
127
|
def neat_numbers(number, roundto = 2) # round to 0 or 2)
|
131
|
-
if roundto == 2
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
128
|
+
number = if roundto == 2
|
129
|
+
format '%.2f', number
|
130
|
+
else
|
131
|
+
number.round
|
132
|
+
end
|
136
133
|
# regex to add commas
|
137
134
|
number.to_s.reverse.gsub(/([0-9]{3}(?=([0-9])))/, '\\1,').reverse
|
138
135
|
end
|
139
136
|
|
140
137
|
# helper to make it easier to do unit conversions on the fly. The definition be called through this measure.
|
141
138
|
def unit_helper(number, from_unit_string, to_unit_string)
|
142
|
-
converted_number = OpenStudio.convert(
|
139
|
+
converted_number = OpenStudio.convert(
|
140
|
+
OpenStudio::Quantity.new(number,
|
141
|
+
OpenStudio.createUnit(from_unit_string).get), OpenStudio.createUnit(to_unit_string).get
|
142
|
+
).get.value
|
143
143
|
end
|
144
144
|
|
145
145
|
# helper that loops through lifecycle costs getting total costs under "Construction" or "Salvage" category and add to counter if occurs during year 0
|
@@ -148,14 +148,12 @@ class AddOverhangsByProjectionFactor < OpenStudio::Measure::ModelMeasure
|
|
148
148
|
objects.each do |object|
|
149
149
|
object_LCCs = object.lifeCycleCosts
|
150
150
|
object_LCCs.each do |object_LCC|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
end
|
155
|
-
end
|
151
|
+
next unless (object_LCC.category == 'Construction') || (object_LCC.category == 'Salvage')
|
152
|
+
|
153
|
+
counter += object_LCC.totalCost if object_LCC.yearsFromStart == 0
|
156
154
|
end
|
157
155
|
end
|
158
|
-
|
156
|
+
counter
|
159
157
|
end
|
160
158
|
|
161
159
|
# counter for year 0 capital costs
|
@@ -201,17 +199,18 @@ class AddOverhangsByProjectionFactor < OpenStudio::Measure::ModelMeasure
|
|
201
199
|
next if s.subSurfaceType == 'TubularDaylightDiffuser'
|
202
200
|
|
203
201
|
# get the absoluteAzimuth for the surface so we can categorize it
|
204
|
-
absoluteAzimuth = OpenStudio.convert(s.azimuth, 'rad',
|
202
|
+
absoluteAzimuth = OpenStudio.convert(s.azimuth, 'rad',
|
203
|
+
'deg').get + s.space.get.directionofRelativeNorth + model.getBuilding.northAxis
|
205
204
|
absoluteAzimuth -= 360.0 until absoluteAzimuth < 360.0
|
206
205
|
|
207
206
|
if facade == 'North'
|
208
|
-
next
|
207
|
+
next unless (absoluteAzimuth >= 315.0) || (absoluteAzimuth < 45.0)
|
209
208
|
elsif facade == 'East'
|
210
|
-
next
|
209
|
+
next unless (absoluteAzimuth >= 45.0) && (absoluteAzimuth < 135.0)
|
211
210
|
elsif facade == 'South'
|
212
|
-
next
|
211
|
+
next unless (absoluteAzimuth >= 135.0) && (absoluteAzimuth < 225.0)
|
213
212
|
elsif facade == 'West'
|
214
|
-
next
|
213
|
+
next unless (absoluteAzimuth >= 225.0) && (absoluteAzimuth < 315.0)
|
215
214
|
else
|
216
215
|
runner.registerError('Unexpected value of facade: ' + facade + '.')
|
217
216
|
return false
|
@@ -238,23 +237,19 @@ class AddOverhangsByProjectionFactor < OpenStudio::Measure::ModelMeasure
|
|
238
237
|
if new_overhang.empty?
|
239
238
|
ok = runner.registerWarning('Unable to add overhang to ' + s.briefDescription +
|
240
239
|
' with projection factor ' + projection_factor.to_s + ' and offset ' + offset.to_s + '.')
|
241
|
-
return false
|
240
|
+
return false unless ok
|
242
241
|
else
|
243
242
|
new_overhang.get.setName("#{s.name} - Overhang")
|
244
243
|
runner.registerInfo('Added overhang ' + new_overhang.get.briefDescription + ' to ' +
|
245
244
|
s.briefDescription + ' with projection factor ' + projection_factor.to_s +
|
246
245
|
' and offset ' + '0' + '.')
|
247
|
-
if construction_chosen
|
248
|
-
if !construction.to_Construction.empty?
|
249
|
-
new_overhang.get.setConstruction(construction)
|
250
|
-
end
|
251
|
-
end
|
246
|
+
new_overhang.get.setConstruction(construction) if construction_chosen && !construction.to_Construction.empty?
|
252
247
|
overhang_added = true
|
253
248
|
end
|
254
249
|
end
|
255
250
|
end
|
256
251
|
|
257
|
-
|
252
|
+
unless overhang_added
|
258
253
|
runner.registerAsNotApplicable("The model has exterior #{facade.downcase} walls, but no windows were found to add overhangs to.")
|
259
254
|
return true
|
260
255
|
end
|
@@ -268,9 +263,11 @@ class AddOverhangsByProjectionFactor < OpenStudio::Measure::ModelMeasure
|
|
268
263
|
final_shading_groups.each do |shading_group|
|
269
264
|
number_of_final_space_shading_surf += shading_group.shadingSurfaces.size
|
270
265
|
end
|
271
|
-
runner.registerFinalCondition("The final building has #{number_of_final_space_shading_surf} space shading surfaces. Initial capital costs associated with the improvements are $#{neat_numbers(
|
266
|
+
runner.registerFinalCondition("The final building has #{number_of_final_space_shading_surf} space shading surfaces. Initial capital costs associated with the improvements are $#{neat_numbers(
|
267
|
+
yr0_capital_totalCosts, 0
|
268
|
+
)}.")
|
272
269
|
|
273
|
-
|
270
|
+
true
|
274
271
|
end
|
275
272
|
end
|
276
273
|
|
@@ -3,8 +3,8 @@
|
|
3
3
|
<schema_version>3.1</schema_version>
|
4
4
|
<name>add_overhangs_by_projection_factor</name>
|
5
5
|
<uid>1e29e117-3916-4368-b24d-75da54045956</uid>
|
6
|
-
<version_id>
|
7
|
-
<version_modified>2025-
|
6
|
+
<version_id>7de48598-b984-4a31-b8b7-bedb9a80e9c2</version_id>
|
7
|
+
<version_modified>2025-09-25T15:33:44Z</version_modified>
|
8
8
|
<xml_checksum>6DE831F7</xml_checksum>
|
9
9
|
<class_name>AddOverhangsByProjectionFactor</class_name>
|
10
10
|
<display_name>Add Overhangs by Projection Factor</display_name>
|
@@ -127,13 +127,13 @@
|
|
127
127
|
<filename>measure.rb</filename>
|
128
128
|
<filetype>rb</filetype>
|
129
129
|
<usage_type>script</usage_type>
|
130
|
-
<checksum>
|
130
|
+
<checksum>5C47B13D</checksum>
|
131
131
|
</file>
|
132
132
|
<file>
|
133
133
|
<filename>AddOverhangsByProjectionFactor_Test.rb</filename>
|
134
134
|
<filetype>rb</filetype>
|
135
135
|
<usage_type>test</usage_type>
|
136
|
-
<checksum>
|
136
|
+
<checksum>E5AFF7A2</checksum>
|
137
137
|
</file>
|
138
138
|
<file>
|
139
139
|
<filename>OverhangTestModel_01.osm</filename>
|
@@ -19,11 +19,11 @@ class EnableDemandControlledVentilation < OpenStudio::Measure::ModelMeasure
|
|
19
19
|
# define the name that a user will see, this method may be deprecated as
|
20
20
|
# the display name in PAT comes from the name field in measure.xml
|
21
21
|
def name
|
22
|
-
|
22
|
+
'Enable Demand Controlled Ventilation'
|
23
23
|
end
|
24
24
|
|
25
25
|
# define the arguments that the user will input
|
26
|
-
def arguments(
|
26
|
+
def arguments(_model)
|
27
27
|
args = OpenStudio::Measure::OSArgumentVector.new
|
28
28
|
|
29
29
|
# make choice argument economizer control type
|
@@ -35,7 +35,7 @@ class EnableDemandControlledVentilation < OpenStudio::Measure::ModelMeasure
|
|
35
35
|
dcv_type.setDisplayName('DCV Type')
|
36
36
|
args << dcv_type
|
37
37
|
|
38
|
-
|
38
|
+
args
|
39
39
|
end
|
40
40
|
|
41
41
|
# define what happens when the measure is cop
|
@@ -43,9 +43,7 @@ class EnableDemandControlledVentilation < OpenStudio::Measure::ModelMeasure
|
|
43
43
|
super(model, runner, user_arguments)
|
44
44
|
|
45
45
|
# use the built-in error checking
|
46
|
-
|
47
|
-
return false
|
48
|
-
end
|
46
|
+
return false unless runner.validateUserArguments(arguments(model), user_arguments)
|
49
47
|
|
50
48
|
# assign the user inputs to variables
|
51
49
|
dcv_type = runner.getStringArgumentValue('dcv_type', user_arguments)
|
@@ -59,11 +57,11 @@ class EnableDemandControlledVentilation < OpenStudio::Measure::ModelMeasure
|
|
59
57
|
|
60
58
|
# short def to make numbers pretty (converts 4125001.25641 to 4,125,001.26 or 4,125,001). The definition be called through this measure
|
61
59
|
def neat_numbers(number, roundto = 2) # round to 0 or 2)
|
62
|
-
if roundto == 2
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
60
|
+
number = if roundto == 2
|
61
|
+
format '%.2f', number
|
62
|
+
else
|
63
|
+
number.round
|
64
|
+
end
|
67
65
|
# regex to add commas
|
68
66
|
number.to_s.reverse.gsub(/([0-9]{3}(?=([0-9])))/, '\\1,').reverse
|
69
67
|
end
|
@@ -76,35 +74,34 @@ class EnableDemandControlledVentilation < OpenStudio::Measure::ModelMeasure
|
|
76
74
|
# find AirLoopHVACOutdoorAirSystem on loop
|
77
75
|
air_loop.supplyComponents.each do |supply_component|
|
78
76
|
hVACComponent = supply_component.to_AirLoopHVACOutdoorAirSystem
|
79
|
-
if
|
80
|
-
|
81
|
-
|
82
|
-
# get ControllerOutdoorAir
|
83
|
-
controller_oa = hVACComponent.getControllerOutdoorAir
|
84
|
-
|
85
|
-
# get ControllerMechanicalVentilation
|
86
|
-
controller_mv = controller_oa.controllerMechanicalVentilation
|
87
|
-
|
88
|
-
if dcv_type == 'EnableDCV'
|
89
|
-
# check if demand control is enabled, if not, then enable it
|
90
|
-
if controller_mv.demandControlledVentilation == true
|
91
|
-
runner.registerInfo("#{air_loop.name} already has DCV enabled.")
|
92
|
-
else
|
93
|
-
controller_mv.setDemandControlledVentilation(true)
|
94
|
-
runner.registerInfo("Enabling DCV for #{air_loop.name}.")
|
95
|
-
air_loops_changed << air_loop
|
96
|
-
end
|
97
|
-
elsif dcv_type == 'DisableDCV'
|
98
|
-
# check if demand control is disabled, if not, then disabled it
|
99
|
-
if controller_mv.demandControlledVentilation == false
|
100
|
-
runner.registerInfo("#{air_loop.name} already has DCV disabled.")
|
101
|
-
else
|
102
|
-
controller_mv.setDemandControlledVentilation(false)
|
103
|
-
runner.registerInfo("Disabling DCV for #{air_loop.name}.")
|
104
|
-
air_loops_changed << air_loop
|
105
|
-
end
|
106
|
-
end
|
77
|
+
next if hVACComponent.empty?
|
78
|
+
|
79
|
+
hVACComponent = hVACComponent.get
|
107
80
|
|
81
|
+
# get ControllerOutdoorAir
|
82
|
+
controller_oa = hVACComponent.getControllerOutdoorAir
|
83
|
+
|
84
|
+
# get ControllerMechanicalVentilation
|
85
|
+
controller_mv = controller_oa.controllerMechanicalVentilation
|
86
|
+
|
87
|
+
if dcv_type == 'EnableDCV'
|
88
|
+
# check if demand control is enabled, if not, then enable it
|
89
|
+
if controller_mv.demandControlledVentilation == true
|
90
|
+
runner.registerInfo("#{air_loop.name} already has DCV enabled.")
|
91
|
+
else
|
92
|
+
controller_mv.setDemandControlledVentilation(true)
|
93
|
+
runner.registerInfo("Enabling DCV for #{air_loop.name}.")
|
94
|
+
air_loops_changed << air_loop
|
95
|
+
end
|
96
|
+
elsif dcv_type == 'DisableDCV'
|
97
|
+
# check if demand control is disabled, if not, then disabled it
|
98
|
+
if controller_mv.demandControlledVentilation == false
|
99
|
+
runner.registerInfo("#{air_loop.name} already has DCV disabled.")
|
100
|
+
else
|
101
|
+
controller_mv.setDemandControlledVentilation(false)
|
102
|
+
runner.registerInfo("Disabling DCV for #{air_loop.name}.")
|
103
|
+
air_loops_changed << air_loop
|
104
|
+
end
|
108
105
|
end
|
109
106
|
end
|
110
107
|
end
|
@@ -118,7 +115,7 @@ class EnableDemandControlledVentilation < OpenStudio::Measure::ModelMeasure
|
|
118
115
|
# Report final condition of model
|
119
116
|
runner.registerFinalCondition("#{air_loops_changed.size} air loops now have demand controlled ventilation enabled.")
|
120
117
|
|
121
|
-
|
118
|
+
true
|
122
119
|
end
|
123
120
|
end
|
124
121
|
|