diff --git a/lib/BOSS/boss.rb b/lib/BOSS/boss.rb
index e562777..f998e7f 100644
--- a/lib/BOSS/boss.rb
+++ b/lib/BOSS/boss.rb
@@ -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.
diff --git a/lib/BOSS/buildingsync_reader/buildingsync_reader.rb b/lib/BOSS/buildingsync_reader/buildingsync_reader.rb
index 028a0b6..d625bd4 100644
--- a/lib/BOSS/buildingsync_reader/buildingsync_reader.rb
+++ b/lib/BOSS/buildingsync_reader/buildingsync_reader.rb
@@ -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 / fenestration_r_value 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
diff --git a/lib/BOSS/buildingsync_reader/systems_map.rb b/lib/BOSS/buildingsync_reader/systems_map.rb
index 32989a5..8ac2fde 100644
--- a/lib/BOSS/buildingsync_reader/systems_map.rb
+++ b/lib/BOSS/buildingsync_reader/systems_map.rb
@@ -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
diff --git a/lib/BOSS/osw_arg_populator.rb b/lib/BOSS/osw_arg_populator.rb
index 2d899d0..070eea5 100644
--- a/lib/BOSS/osw_arg_populator.rb
+++ b/lib/BOSS/osw_arg_populator.rb
@@ -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) }
diff --git a/spec/tests/unit/buildingsync_reader_spec.rb b/spec/tests/unit/buildingsync_reader_spec.rb
index d7317d0..91d9d2a 100644
--- a/spec/tests/unit/buildingsync_reader_spec.rb
+++ b/spec/tests/unit/buildingsync_reader_spec.rb
@@ -23,6 +23,24 @@ def wrap_in_site(xml)
XML
end
+def wrap_in_systems(xml)
+ <<~XML
+
+
+
+
+
+
+
+
+ #{xml}
+
+
+
+
+ XML
+end
+
RSpec.describe 'BuildingSyncReader' do
describe 'get_climate_zone should' do
it "get from site" do
@@ -334,24 +352,6 @@ def wrap_in_systems(xml)
end
describe 'get_total_weighted_average_load should' do
- def wrap_in_systems(xml)
- <<~XML
-
-
-
-
-
-
-
-
- #{xml}
-
-
-
-
- XML
- end
-
it "sum weighted average load" do
# Set Up
doc = REXML::Document.new wrap_in_systems(<<~XML)
@@ -423,4 +423,134 @@ def wrap_in_systems(xml)
expect(buidingsync_reader.get_total_weighted_average_load).to eq nil
end
end
+
+ describe 'get_window_data should' do
+ good_window = <<~XML
+
+
+
+
+ Aluminum no thermal break
+ false
+ Average
+ Clear uncoated
+ Single pane
+ 0.391000
+ 0.391000
+ 3.241000
+
+ XML
+ invalid_window = <<~XML
+
+
+
+
+ Fiberglass
+ false
+ Average
+ Clear uncoated
+ Single pane
+ 0.391000
+ 0.391000
+ 3.241000
+
+ XML
+ door = <<~XML
+
+
+
+ Uninsulated metal
+
+
+
+ XML
+
+ it "work in happy case" do
+ # Set Up
+ doc = REXML::Document.new wrap_in_systems(<<~XML)
+
+ #{good_window}
+
+ XML
+
+ # Action
+ buidingsync_reader = BOSS::BuildingSyncReader.new(doc, nil, ASHRAE90_1)
+ window_pane_type, fenestration_u_factor, solar_heat_gain_coefficient, visible_transmittance = buidingsync_reader.get_window_data
+
+ # Assertion
+ expect(window_pane_type).to eq "Single - No LowE - Clear - Aluminum"
+ expect(fenestration_u_factor).to eq "3.241000"
+ expect(solar_heat_gain_coefficient).to eq "0.391000"
+ expect(visible_transmittance).to eq "0.391000"
+ end
+
+ it "ignore doors and skylights" do
+ # Set Up
+ doc = REXML::Document.new wrap_in_systems(<<~XML)
+
+ #{door}
+ #{good_window}
+
+ XML
+
+ # Action
+ buidingsync_reader = BOSS::BuildingSyncReader.new(doc, nil, ASHRAE90_1)
+ window_pane_type, fenestration_u_factor, solar_heat_gain_coefficient, visible_transmittance = buidingsync_reader.get_window_data
+
+ # Assertion
+ expect(window_pane_type).to eq "Single - No LowE - Clear - Aluminum"
+ expect(fenestration_u_factor).to eq "3.241000"
+ expect(solar_heat_gain_coefficient).to eq "0.391000"
+ expect(visible_transmittance).to eq "0.391000"
+ end
+
+ it "work if first window is invalid but second isnt" do
+ # Set Up
+ doc = REXML::Document.new wrap_in_systems(<<~XML)
+
+ #{invalid_window}
+ #{good_window}
+
+ XML
+
+ # Action
+ buidingsync_reader = BOSS::BuildingSyncReader.new(doc, nil, ASHRAE90_1)
+ window_pane_type, fenestration_u_factor, solar_heat_gain_coefficient, visible_transmittance = buidingsync_reader.get_window_data
+
+ # Assertion
+ expect(window_pane_type).to eq "Single - No LowE - Clear - Aluminum"
+ expect(fenestration_u_factor).to eq "3.241000"
+ expect(solar_heat_gain_coefficient).to eq "0.391000"
+ expect(visible_transmittance).to eq "0.391000"
+ end
+
+ it "return nil if there are no windows" do
+ # Set Up
+ doc = REXML::Document.new wrap_in_systems("")
+
+ # Action
+ buidingsync_reader = BOSS::BuildingSyncReader.new(doc, nil, ASHRAE90_1)
+ window_pane_type, fenestration_u_factor, solar_heat_gain_coefficient, visible_transmittance = buidingsync_reader.get_window_data
+
+ # Assertion
+ expect(window_pane_type).to eq nil
+ end
+
+ # warn not error
+ it "return nil if all windows are invalid" do
+ # Set Up
+ doc = REXML::Document.new wrap_in_systems(<<~XML)
+
+ #{invalid_window}
+
+ XML
+
+ # Action
+ buidingsync_reader = BOSS::BuildingSyncReader.new(doc, nil, ASHRAE90_1)
+ window_pane_type, fenestration_u_factor, solar_heat_gain_coefficient, visible_transmittance = buidingsync_reader.get_window_data
+
+ # Assertion
+ expect(window_pane_type).to eq nil
+ end
+ end
end