Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ jobs:
gem install bundler -v 2.4.10
bundle config set --local path "$GITHUB_WORKSPACE/.bundle/install"
bundle install
bundle exec rake measures:install_external
- name: Run Tests
run: bundle exec rspec
2 changes: 2 additions & 0 deletions lib/BOSS/boss.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ def write_baseline_osw(xml_file_path, output_dir, epw_file_path, standard_to_be_

OSWArgPopulator::populate_set_lighting_loads_by_LPD_args(baseline_osw, bsync_reader)
OSWArgPopulator::populate_set_electric_equipment_loads_by_epd_args(baseline_osw, bsync_reader)
OSWArgPopulator::populate_replace_baseline_windows_args(baseline_osw, bsync_reader)

OSWArgPopulator::populate_openstudio_results_args(baseline_osw, bsync_reader)

# Gather extension-derived paths (typically gem-based measures/files) from ObjectSpace.
Expand Down
55 changes: 51 additions & 4 deletions lib/BOSS/buildingsync_reader/buildingsync_reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,11 @@ def _get_total_number_floors
# 1. /YearOfLastMajorRemodel of building
# 2. /YearOfConstruction of building
def get_built_year
year_of_major_remodel = @building_xml.elements["#{@ns}YearOfLastMajorRemodel"]&.text.to_f
return year_of_major_remodel if !year_of_major_remodel.nil?
year_of_major_remodel = @building_xml.elements["#{@ns}YearOfLastMajorRemodel"]&.text
return year_of_major_remodel.to_f if !year_of_major_remodel.nil?

year_of_construction = @building_xml.elements["#{@ns}YearOfConstruction"]&.text.to_f
return year_of_construction if !year_of_construction.nil?
year_of_construction = @building_xml.elements["#{@ns}YearOfConstruction"]&.text
return year_of_construction.to_f if !year_of_construction.nil?
end

# map year built and standard_to_be_used to a standard_template
Expand Down Expand Up @@ -310,5 +310,52 @@ def get_total_weighted_average_load
return nil if !all_weighted_average_loads.all?
return all_weighted_average_loads.map {|s| s.to_f}.sum
end

# first valid window that has all mappable fields
def get_window_data
# if no windows, return nil
fenestration_systems = @facility_xml.elements.each("#{@ns}Systems/#{@ns}FenestrationSystems/#{@ns}FenestrationSystem") {|s| s}
return nil if fenestration_systems.nil?

# get all windows
windows = fenestration_systems.select {|fs| fs.elements["#{@ns}FenestrationType/#{@ns}Window"] }
return nil if windows.empty?

# iter through windows until we get one with all the required fields
windows.each do |window|
# get data
fenestration_frame_material = window.elements["#{@ns}FenestrationFrameMaterial"]&.text
glass_type = window.elements["#{@ns}GlassType"]&.text
fenestration_glass_layers = window.elements["#{@ns}FenestrationGlassLayers"]&.text
solar_heat_gain_coefficient = window.elements["#{@ns}SolarHeatGainCoefficient"]&.text
visible_transmittance = window.elements["#{@ns}VisibleTransmittance"]&.text

# We only need one of fenestration_u_factor/fenestration_r_value
fenestration_u_factor = window.elements["#{@ns}FenestrationUFactor"]&.text
if fenestration_u_factor.nil?
fenestration_r_value = window.elements["#{@ns}FenestrationRValue"]&.text
if !fenestration_r_value.nil? then fenestration_u_factor = (1.0 / fenestration_r_value.to_f).to_s end
end

# map data
os_fenestration_frame_material = BuildingSyncToOSSystemMaps.get_frame_material_map[fenestration_frame_material.to_s]
os_glass_type = BuildingSyncToOSSystemMaps.get_glass_type_map[glass_type.to_s]
os_fenestration_glass_layers = BuildingSyncToOSSystemMaps.get_glass_layers_map[fenestration_glass_layers.to_s]

# return if we can use this one
if (
!os_fenestration_frame_material.nil? &&
!os_glass_type.nil? &&
!os_fenestration_glass_layers.nil? &&
!fenestration_u_factor.nil? &&
!solar_heat_gain_coefficient.nil? &&
!visible_transmittance.nil?
)
window_pane_type = [os_fenestration_glass_layers, os_glass_type, os_fenestration_frame_material].join(' - ')
return window_pane_type, fenestration_u_factor, solar_heat_gain_coefficient, visible_transmittance
end
end
return nil
end
end
end
38 changes: 38 additions & 0 deletions lib/BOSS/buildingsync_reader/systems_map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,42 @@ def self.get_hvac_map
"Other" => ""
}
end

def self.get_glass_layers_map
return {
"Single pane" => "Single",
"Double pane" => "Double",
"Triple pane" => "Triple",
"Single paned with storm panel" => "Single",
}
end

def self.get_glass_type_map
return {
"Clear uncoated" => "No LowE - Clear",
"Low e" => "LowE - Clear",
"Tinted" => "No LowE - Tinted/Reflective",
"Tinted plus low e" => "LowE - Tinted/Reflective",
"Reflective" => "No LowE - Tinted/Reflective",
"Reflective on tint" => "No LowE - Tinted/Reflective",
"High performance tint" => "LowE - Tinted/Reflective",
"Sunbelt low E low SHGC" => "LowE - Tinted/Reflective",
# "Suspended film" => "",
# "Plastic" => "",
}
end

def self.get_frame_material_map
return {
"Aluminum uncategorized" => "Aluminum",
"Aluminum no thermal break" => "Aluminum",
"Aluminum thermal break" => "Thermally Broken Aluminum",
# "Clad" => "",
# "Composite" => "",
# "Fiberglass" => "",
# "Steel" => "",
# "Vinyl" => "",
"Wood" => "Wood",
}
end
end
22 changes: 22 additions & 0 deletions lib/BOSS/osw_arg_populator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,28 @@ def self.populate_set_electric_equipment_loads_by_epd_args(osw, bsync_reader)
# om_frequency
end

def self.populate_replace_baseline_windows_args(osw, bsync_reader)
osw[:steps].append({"measure_dir_name": "replace_baseline_windows", "arguments": {}})
set_measure_argument = lambda {| key, value | OpenStudio::Extension.set_measure_argument(osw, "replace_baseline_windows", key, value) }

# skip if no window_data
window_data = bsync_reader.get_window_data
if window_data.nil?
set_measure_argument.call("__SKIP__", true)
return
end
window_pane_type, fenestration_u_factor, solar_heat_gain_coefficient, visible_transmittance = window_data

# Add args
# - __SKIP__
set_measure_argument.call("__SKIP__", false)
set_measure_argument.call("window_pane_type", window_pane_type)
set_measure_argument.call("u_value_ip", fenestration_u_factor)
set_measure_argument.call("shgc", solar_heat_gain_coefficient)
set_measure_argument.call("vlt", visible_transmittance)

end

def self.populate_openstudio_results_args(osw, bsync_reader)
osw[:steps].append({"measure_dir_name": "openstudio_results", "arguments": {}})
set_measure_argument = lambda {| key, value | OpenStudio::Extension.set_measure_argument(osw, "openstudio_results", key, value) }
Expand Down
124 changes: 124 additions & 0 deletions spec/files/v2.7.0/all_measures_applied_example.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
All-measures example BuildingSync file for BOSS integration testing.
Designed so that every currently supported measure path is exercised
(i.e. none are skipped). Add content here when a new measure is added.

Measures covered:
set_run_period - always applied; no BSync data needed
ChangeBuildingLocation - Address (City/State) + ClimateZoneType (ASHRAE)
create_bar_from_building_type_ratios - OccupancyClassification, FloorAreas, floors,
YearOfConstruction, AspectRatio, Sections/FloorToFloorHeight
create_typical_building_from_model - HVACSystem/PrincipalHVACSystemType
SetLightingLoadsByLPD - LightingSystem/InstalledPower
set_electric_equipment_loads_by_epd - PlugLoad/WeightedAverageLoad
replace_baseline_windows - FenestrationSystem with Window and all mappable fields
openstudio_results - always applied; no BSync data needed
-->
<auc:BuildingSync version="2.7.0"
xmlns:auc="http://buildingsync.net/schemas/bedes-auc/2019">
<auc:Facilities>
<auc:Facility ID="Facility-1">
<auc:Sites>
<auc:Site ID="Site-1">
<auc:Buildings>
<auc:Building ID="Building-1">
<auc:PremisesName>All Measures Example Building</auc:PremisesName>

<!-- ChangeBuildingLocation: city/state for weather file lookup -->
<auc:Address>
<auc:City>Denver</auc:City>
<auc:State>CO</auc:State>
</auc:Address>

<!-- ChangeBuildingLocation: fallback climate zone if city lookup fails -->
<auc:ClimateZoneType>
<auc:ASHRAE>
<auc:ClimateZone>5B</auc:ClimateZone>
</auc:ASHRAE>
</auc:ClimateZoneType>

<!-- create_bar_from_building_type_ratios: occupancy type -->
<!-- "Office" + 15,000 ft2 maps to SmallOffice (ASHRAE90.1, max 20,000 ft2) -->
<auc:OccupancyClassification>Office</auc:OccupancyClassification>

<!-- create_bar_from_building_type_ratios: floor counts -->
<auc:FloorsAboveGrade>2</auc:FloorsAboveGrade>
<auc:FloorsBelowGrade>0</auc:FloorsBelowGrade>

<!-- create_bar_from_building_type_ratios + SetLightingLoadsByLPD
+ set_electric_equipment_loads_by_epd: total floor area -->
<auc:FloorAreas>
<auc:FloorArea>
<auc:FloorAreaType>Gross</auc:FloorAreaType>
<auc:FloorAreaValue>15000.0</auc:FloorAreaValue>
</auc:FloorArea>
</auc:FloorAreas>

<!-- create_bar_from_building_type_ratios: footprint shape -->
<auc:AspectRatio>1.5</auc:AspectRatio>

<!-- create_bar_from_building_type_ratios: standard template
2000 -> "DOE Ref 1980-2004" for ASHRAE90.1 -->
<auc:YearOfConstruction>2000</auc:YearOfConstruction>

<!-- create_bar_from_building_type_ratios: floor-to-floor height (ft) -->
<auc:Sections>
<auc:Section ID="Section-1">
<auc:FloorToFloorHeight>10.0</auc:FloorToFloorHeight>
</auc:Section>
</auc:Sections>
</auc:Building>
</auc:Buildings>
</auc:Site>
</auc:Sites>

<auc:Systems>

<!-- create_typical_building_from_model:
"Packaged Rooftop Air Conditioner" -> "PSZ-AC with gas unit heaters" -->
<auc:HVACSystems>
<auc:HVACSystem ID="HVACSystem-1">
<auc:PrincipalHVACSystemType>Packaged Rooftop Air Conditioner</auc:PrincipalHVACSystemType>
</auc:HVACSystem>
</auc:HVACSystems>

<!-- SetLightingLoadsByLPD:
InstalledPower in kW; LPD = 15 kW * 1000 / 15,000 ft2 = 1.0 W/ft2 -->
<auc:LightingSystems>
<auc:LightingSystem ID="LightingSystem-1">
<auc:InstalledPower>15</auc:InstalledPower>
</auc:LightingSystem>
</auc:LightingSystems>

<!-- replace_baseline_windows: first Window with all mappable fields is used.
Frame: "Aluminum no thermal break" -> "Aluminum"
Glass: "Clear uncoated" -> "No LowE - Clear"
Layers: "Double pane" -> "Double"
Result: window_pane_type = "Double - No LowE - Clear - Aluminum" -->
<auc:FenestrationSystems>
<auc:FenestrationSystem ID="FenestrationSystem-1">
<auc:FenestrationType>
<auc:Window/>
</auc:FenestrationType>
<auc:FenestrationFrameMaterial>Aluminum no thermal break</auc:FenestrationFrameMaterial>
<auc:GlassType>Clear uncoated</auc:GlassType>
<auc:FenestrationGlassLayers>Double pane</auc:FenestrationGlassLayers>
<auc:FenestrationUFactor>0.50</auc:FenestrationUFactor>
<auc:SolarHeatGainCoefficient>0.39</auc:SolarHeatGainCoefficient>
<auc:VisibleTransmittance>0.39</auc:VisibleTransmittance>
</auc:FenestrationSystem>
</auc:FenestrationSystems>

<!-- set_electric_equipment_loads_by_epd:
WeightedAverageLoad in W; EPD = 15,000 W / 15,000 ft2 = 1.0 W/ft2 -->
<auc:PlugLoads>
<auc:PlugLoad ID="PlugLoad-1">
<auc:WeightedAverageLoad>15000</auc:WeightedAverageLoad>
</auc:PlugLoad>
</auc:PlugLoads>

</auc:Systems>
</auc:Facility>
</auc:Facilities>
</auc:BuildingSync>
1 change: 1 addition & 0 deletions spec/tests/integration/write_and_run_osws_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

test_configs = [
# file_name, standard, epw_path, schema_version
['all_measures_applied_example.xml', ASHRAE90_1, nil, 'v2.7.0'], # exercises every measure path; uncomment to run full simulation
['179D_Example_Building.xml', ASHRAE90_1, nil, 'v2.7.0'],
['BETTER-1.0.0_SampleOffice_gemtest.xml', ASHRAE90_1, nil, 'v2.7.0'],
['building_151.xml', ASHRAE90_1, nil, 'v2.7.0'],
Expand Down
Loading
Loading