diff --git a/README.md b/README.md index 9c5856d1..07121ee1 100644 --- a/README.md +++ b/README.md @@ -20,15 +20,41 @@ Or install it yourself as: ## Usage -To be filled out later. +The BuildingSync-Gem +- converts your BuildingSync.xml file into + - an OpenStudio Baseline model + - an OpenStudio workflow for each scenario defined in the XML file +- enables simulation of the baseline model and all workflows and +- inserts simulation results back into the xml file. + +All these features are driven by the translator class. + +```ruby +# initializing the translator +translator = BuildingSync::Translator.new(building_sync_xml_file_path, out_path) +# generating the OpenStudio Model and writing the osm file +translator.write_osm +# generating the OpenStudio workflows and writing the osw files +translator.write_osws +# running the baseline simulations +translator.run_osm +# running all simulations +translator.run_osws +# gather the results and save them to an BuildingSync.XML +translator.gather_results(out_path) +``` +## Testing + +Check out the repository and then execute: +```ruby +$ bundle install +$ bundle update +$ bundle exec rake +``` ## TODO -- [ ] Add initial BuildingSync class (can use [BRICR](https://github.com/NREL/bricr/blob/develop/lib/bricr/building_sync.rb) class as example). Use REXML for reading and writing BSync. -- [ ] Add ForwardTranslator class (following [other OpenStudio conventions](https://github.com/NREL/OpenStudio/blob/develop/openstudiocore/src/gbxml/ForwardTranslator.hpp)) that translates BuildingSync to OpenStudio (using pure Ruby) - [ ] Move BuildingSync specific measures into this gem. See list from [here](https://docs.google.com/spreadsheets/d/1PCB4nZoLQ1cWhnlrlnHwo9kI4G8ChOeblU3L4uZu7bc/edit#gid=1482405742) -- [ ] Add example on how to use some code from ```openstudio-standards``` or ```openstudio-model-articulation``` during the translation -- [ ] Add unit test for BuildingSync -> OSM translation - [ ] Add ability to perform validation using https://selectiontool.buildingsync.net. Return which use cases existing BuildingSync XML is valid for. diff --git a/lib/buildingsync/extension.rb b/lib/buildingsync/extension.rb index 95668633..97342305 100644 --- a/lib/buildingsync/extension.rb +++ b/lib/buildingsync/extension.rb @@ -52,6 +52,7 @@ class Extension < OpenStudio::Extension::Extension # number of parallel BuildingSync files to run NUM_BUILDINGS_PARALLEL = 2 + ## # Override the base class # The Extension class contains both the instance of the BuildingSync file (in XML) and the # helper methods from the OpenStudio::Extension gem to support managing measures that are related @@ -62,33 +63,5 @@ def initialize super @root_dir = File.absolute_path(File.join(File.dirname(__FILE__), '..', '..')) end - - # Read in an existing buildingsync file - # - # @param buildingsync_file [string]: path to BuildingSync XML - def self.from_file(buildingsync_file) - bsync = Extension.new - bsync.read_from_xml(buildingsync_file) - return bsync - end - - # read the XML from file - def read_from_xml(buildingsync_file) - return nil - end - - # write OSW file - # This method will write a single OSW from the BuildingSync file. The OSW will not include any of the scenarios - # other than the baseline. - def to_osw - return nil - end - - # write multiple OSW files - # This method will write out multiple OSW files from the BuildingSync file. The OSWs will be constructed based - # on the various scenarios that are in the BuildingSync file - def to_osws - return nil - end end end diff --git a/lib/buildingsync/generator.rb b/lib/buildingsync/generator.rb index 042c07f5..13634d5b 100644 --- a/lib/buildingsync/generator.rb +++ b/lib/buildingsync/generator.rb @@ -37,6 +37,15 @@ module BuildingSync class Generator + ## + # creates a minimum building sync snippet + ## + # @param occupancy_classification [string] + # @param year_of_const [int] + # @param floor_area_type [string] + # @param floor_area_value [float] + # @param ns [string] + # @return REXML::Document def create_minimum_snippet(occupancy_classification, year_of_const, floor_area_type, floor_area_value, ns = 'auc') xml_path = File.expand_path('./../../spec/files/building_151_Blank.xml', File.dirname(__FILE__)) @@ -72,6 +81,14 @@ def create_minimum_snippet(occupancy_classification, year_of_const, floor_area_t return doc end + ## + # creates a minimum facility + ## + # @param occupancy_classification [string] + # @param year_of_const [int] + # @param floor_area_type [string] + # @param floor_area_value [float] + # @return BuildingSync::Facility def create_minimum_facility(occupancy_classification, year_of_const, floor_area_type, floor_area_value) xml_snippet = create_minimum_snippet(occupancy_classification, year_of_const, floor_area_type, floor_area_value) ns = 'auc' diff --git a/lib/buildingsync/helpers/helper.rb b/lib/buildingsync/helpers/helper.rb index 2aa6c540..ef8cd633 100644 --- a/lib/buildingsync/helpers/helper.rb +++ b/lib/buildingsync/helpers/helper.rb @@ -36,6 +36,10 @@ # ******************************************************************************* module BuildingSync class Helper + ## + # get text value from xml element + # @param xml_element [REXML::Element] + # @return string def self.get_text_value(xml_element) if xml_element return xml_element.text @@ -43,6 +47,10 @@ def self.get_text_value(xml_element) return nil end + ## + # get date value from xml element + # @param xml_element [REXML::Element] + # @return string def self.get_date_value(xml_element) if xml_element return Date.parse(xml_element.text) @@ -50,6 +58,10 @@ def self.get_date_value(xml_element) return nil end + ## + # get zone name list + # @param zones [array] + # @return array def self.get_zone_name_list(zones) names = [] zones.each do |zone| @@ -58,10 +70,14 @@ def self.get_zone_name_list(zones) return names end + ## + # read xml file document + # @param xml_file_path [string] + # @return REXML::Document def self.read_xml_file_document(xml_file_path) doc = nil File.open(xml_file_path, 'r') do |file_content| - doc = REXML::Document.new(file_content, { :ignore_whitespace_nodes => :all }) + doc = REXML::Document.new(file_content, :ignore_whitespace_nodes => :all) end return doc end diff --git a/lib/buildingsync/model_articulation/building.rb b/lib/buildingsync/model_articulation/building.rb index 3cae829e..5ed53e75 100644 --- a/lib/buildingsync/model_articulation/building.rb +++ b/lib/buildingsync/model_articulation/building.rb @@ -35,13 +35,14 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # ******************************************************************************* require_relative 'building_section' +require_relative 'location_element' require_relative '../../../lib/buildingsync/get_bcl_weather_file' require 'date' require 'openstudio/extension/core/os_lib_helper_methods' require 'openstudio/extension/core/os_lib_model_generation' module BuildingSync - class Building < SpatialElement + class Building < LocationElement include OsLib_HelperMethods include EnergyPlus include OsLib_ModelGeneration @@ -95,8 +96,9 @@ def read_xml(build_element, site_occupancy_type, site_total_floor_area, ns) if build_element.attributes['ID'] @id = build_element.attributes['ID'] end - # city and state - read_city_and_state_name(build_element, ns) + + # read location specific values + read_location_values(build_element, ns) # floor areas read_floor_areas(build_element, site_total_floor_area, ns) # standard template @@ -230,6 +232,16 @@ def get_building_type end end + def get_climate_zone(standard_to_be_used = nil) + if standard_to_be_used == ASHRAE90_1 + return @climate_zone_ashrae + elsif standard_to_be_used == CA_TITLE24 + return @climate_zone_ca_t24 + else + return @climate_zone + end + end + def set_building_form_defaults # if aspect ratio, story height or wwr have argument value of 0 then use smart building type defaults building_form_defaults = building_form_defaults(get_building_type) @@ -1044,7 +1056,7 @@ def write_parameters_to_xml(ns, building) add_user_defined_field_to_xml_file(building, ns, 'PartyWallFraction', @party_wall_fraction) add_user_defined_field_to_xml_file(building, ns, 'FractionArea', @fraction_area) - write_parameters_to_xml_for_spatial_element(ns, building) + write_parameters_to_xml_for_spatial_element(building, ns) end def get_space_types @@ -1052,7 +1064,7 @@ def get_space_types end def get_peak_occupancy - peak_occupancy = Hash.new + peak_occupancy = {} if @occupant_quantity peak_occupancy[@id] = @occupant_quantity.to_f return peak_occupancy diff --git a/lib/buildingsync/model_articulation/building_section.rb b/lib/buildingsync/model_articulation/building_section.rb index e8ecd7c3..331b5b48 100644 --- a/lib/buildingsync/model_articulation/building_section.rb +++ b/lib/buildingsync/model_articulation/building_section.rb @@ -245,7 +245,7 @@ def write_parameters_to_xml(ns, building_section) add_user_defined_field_to_xml_file(building_section, ns, 'BuildingType', @bldg_type) add_user_defined_field_to_xml_file(building_section, ns, 'FractionArea', @fraction_area) - write_parameters_to_xml_for_spatial_element(ns, building_section) + write_parameters_to_xml_for_spatial_element(building_section, ns) end def set_bldg_and_system_type diff --git a/lib/buildingsync/model_articulation/location_element.rb b/lib/buildingsync/model_articulation/location_element.rb new file mode 100644 index 00000000..0e8bf9a0 --- /dev/null +++ b/lib/buildingsync/model_articulation/location_element.rb @@ -0,0 +1,173 @@ +# ******************************************************************************* +# OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC. +# BuildingSync(R), Copyright (c) 2015-2020, Alliance for Sustainable Energy, LLC. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# (1) Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# (2) Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# (3) Neither the name of the copyright holder nor the names of any contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission from the respective party. +# +# (4) Other than as required in clauses (1) and (2), distributions in any form +# of modifications or other derivative works may not use the "OpenStudio" +# trademark, "OS", "os", or any other confusingly similar designation without +# specific prior written permission from Alliance for Sustainable Energy, LLC. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE +# UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF +# THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ******************************************************************************* + +module BuildingSync + # base class for objects that will configure workflows based on building sync files + class LocationElement < SpatialElement + + ## + # initialize LocationElement class + def initialize + @climate_zone = nil + @climate_zone_ashrae = nil + @climate_zone_ca_t24 = nil + @weather_file_name = nil + @weather_station_id = nil + @city_name = nil + @state_name = nil + @latitude = nil + @longitude = nil + @street_address = nil + @postal_code = nil + end + + ## + # read location values + ## + # @param build_element [REXML::Element] + # @param ns [string] + def read_location_values(build_element, ns) + # read in the ASHRAE climate zone + read_climate_zone(build_element, ns) + # read in the weather station name + read_weather_file_name(build_element, ns) + # read city and state name + read_city_and_state_name(build_element, ns) + # read latitude and longitude + read_latitude_and_longitude(build_element, ns) + # read site address + read_address_postal_code_notes(build_element, ns) + end + + ## + # read climate zone + ## + # @param build_element [REXML::Element] + # @param ns [string] + def read_climate_zone(build_element, ns) + if build_element.elements["#{ns}:ClimateZoneType/#{ns}:ASHRAE"] + @climate_zone_ashrae = build_element.elements["#{ns}:ClimateZoneType/#{ns}:ASHRAE/#{ns}:ClimateZone"].text + else + @climate_zone_ashrae = nil + end + if build_element.elements["#{ns}:ClimateZoneType/#{ns}:CaliforniaTitle24"] + @climate_zone_ca_t24 = build_element.elements["#{ns}:ClimateZoneType/#{ns}:CaliforniaTitle24/#{ns}:ClimateZone"].text + else + @climate_zone_ca_t24 = nil + end + end + + ## + # read weather file name + ## + # @param build_element [REXML::Element] + # @param ns [string] + def read_weather_file_name(build_element, ns) + if build_element.elements["#{ns}:WeatherStationName"] + @weather_file_name = build_element.elements["#{ns}:WeatherStationName"].text + else + @weather_file_name = nil + end + if build_element.elements["#{ns}:WeatherDataStationID"] + @weather_station_id = build_element.elements["#{ns}:WeatherDataStationID"].text + else + @weather_station_id = nil + end + end + + ## + # read city and state name + ## + # @param build_element [REXML::Element] + # @param ns [string] + def read_city_and_state_name(build_element, ns) + if build_element.elements["#{ns}:Address/#{ns}:City"] + @city_name = build_element.elements["#{ns}:Address/#{ns}:City"].text + else + @city_name = nil + end + if build_element.elements["#{ns}:Address/#{ns}:State"] + @state_name = build_element.elements["#{ns}:Address/#{ns}:State"].text + else + @state_name = nil + end + end + + ## + # read address, postal code and premises notes + ## + # @param build_element [REXML::Element] + # @param ns [string] + def read_address_postal_code_notes(build_element, ns) + if build_element.elements["#{ns}:Address/#{ns}:StreetAddressDetail/#{ns}:Simplified/#{ns}:StreetAddress"] + @street_address = build_element.elements["#{ns}:Address/#{ns}:StreetAddressDetail/#{ns}:Simplified/#{ns}:StreetAddress"].text + else + @street_address = nil + end + + if build_element.elements["#{ns}:Address/#{ns}:PostalCode"] + @postal_code = build_element.elements["#{ns}:Address/#{ns}:PostalCode"].text.to_i + else + @postal_code = nil + end + + if build_element.elements["#{ns}:PremisesNotes"] + @premises_notes = build_element.elements["#{ns}:PremisesNotes"].text + else + @premises_notes = nil + end + end + + ## + # read latitude and longitude + ## + # @param build_element [REXML::Element] + # @param ns [string] + def read_latitude_and_longitude(build_element, ns) + if build_element.elements["#{ns}:Latitude"] + @latitude = build_element.elements["#{ns}:Latitude"].text + else + @latitude = nil + end + if build_element.elements["#{ns}:Longitude"] + @longitude = build_element.elements["#{ns}:Longitude"].text + else + @longitude = nil + end + end + end +end diff --git a/lib/buildingsync/model_articulation/site.rb b/lib/buildingsync/model_articulation/site.rb index bfae345b..69bb1d35 100644 --- a/lib/buildingsync/model_articulation/site.rb +++ b/lib/buildingsync/model_articulation/site.rb @@ -36,7 +36,7 @@ # ******************************************************************************* require_relative 'building' module BuildingSync - class Site < SpatialElement + class Site < LocationElement # initialize def initialize(build_element, ns) # code to initialize @@ -46,26 +46,10 @@ def initialize(build_element, ns) @premises_notes = nil @all_set = false - - # parameter to read and write. - @climate_zone = nil - @climate_zone_ashrae = nil - @climate_zone_ca_t24 = nil - @weather_file_name = nil - @weather_station_id = nil - @city_name = nil - @state_name = nil - @latitude = nil - @longitude = nil - @street_address = nil - @postal_code = nil - - # TM: just use the XML snippet to search for the buildings on the site read_xml(build_element, ns) end - # adding a site to the facility def read_xml(build_element, ns) # first we check if the number of buildings is ok number_of_buildings = 0 @@ -83,16 +67,8 @@ def read_xml(build_element, ns) @occupancy_type = read_occupancy_type(build_element, nil, ns) # check floor areas at the site level @total_floor_area = read_floor_areas(build_element, nil, ns) - # read in the ASHRAE climate zone - read_climate_zone(build_element, ns) - # read in the weather station name - read_weather_file_name(build_element, ns) - # read city and state name - read_city_and_state_name(build_element, ns) - # read latitude and longitude - read_latitude_and_longitude(build_element, ns) - # read site address - read_site_other_details(build_element, ns) + # read location specific values + read_location_values(build_element, ns) # code to create a building build_element.elements.each("#{ns}:Buildings/#{ns}:Building") do |buildings_element| @buildings.push(Building.new(buildings_element, @occupancy_type, @total_floor_area, ns)) @@ -106,81 +82,6 @@ def set_all end end - def read_climate_zone(build_element, ns) - if build_element.elements["#{ns}:ClimateZoneType/#{ns}:ASHRAE"] - @climate_zone_ashrae = build_element.elements["#{ns}:ClimateZoneType/#{ns}:ASHRAE/#{ns}:ClimateZone"].text - else - @climate_zone_ashrae = nil - end - if build_element.elements["#{ns}:ClimateZoneType/#{ns}:CaliforniaTitle24"] - @climate_zone_ca_t24 = build_element.elements["#{ns}:ClimateZoneType/#{ns}:CaliforniaTitle24/#{ns}:ClimateZone"].text - else - @climate_zone_ca_t24 = nil - end - if @climate_zone_ca_t24.nil? && @climate_zone_ashrae.nil? - OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.Site.read_climate_zone', 'Could not find a climate zone in the BuildingSync file.') - end - end - - def read_weather_file_name(build_element, ns) - if build_element.elements["#{ns}:WeatherStationName"] - @weather_file_name = build_element.elements["#{ns}:WeatherStationName"].text - else - @weather_file_name = nil - end - if build_element.elements["#{ns}:WeatherDataStationID"] - @weather_station_id = build_element.elements["#{ns}:WeatherDataStationID"].text - else - @weather_station_id = nil - end - end - - def read_city_and_state_name(build_element, ns) - if build_element.elements["#{ns}:Address/#{ns}:City"] - @city_name = build_element.elements["#{ns}:Address/#{ns}:City"].text - else - @city_name = nil - end - if build_element.elements["#{ns}:Address/#{ns}:State"] - @state_name = build_element.elements["#{ns}:Address/#{ns}:State"].text - else - @state_name = nil - end - end - - def read_site_other_details(build_element, ns) - if build_element.elements["#{ns}:Address/#{ns}:StreetAddressDetail/#{ns}:Simplified/#{ns}:StreetAddress"] - @street_address = build_element.elements["#{ns}:Address/#{ns}:StreetAddressDetail/#{ns}:Simplified/#{ns}:StreetAddress"].text - else - @street_address = nil - end - - if build_element.elements["#{ns}:Address/#{ns}:PostalCode"] - @postal_code = build_element.elements["#{ns}:Address/#{ns}:PostalCode"].text.to_i - else - @postal_code = nil - end - - if build_element.elements["#{ns}:PremisesNotes"] - @premises_notes = build_element.elements["#{ns}:PremisesNotes"].text - else - @premises_notes = nil - end - end - - def read_latitude_and_longitude(build_element, ns) - if build_element.elements["#{ns}:Latitude"] - @latitude = build_element.elements["#{ns}:Latitude"].text - else - @latitude = nil - end - if build_element.elements["#{ns}:Longitude"] - @longitude = build_element.elements["#{ns}:Longitude"].text - else - @longitude = nil - end - end - def build_zone_hash return get_largest_building.build_zone_hash end @@ -236,7 +137,11 @@ def get_building_type end def get_climate_zone - return @climate_zone + if @climate_zone.nil? + return get_largest_building.get_climate_zone + else + return @climate_zone + end end def get_building_objects @@ -272,6 +177,8 @@ def generate_baseline_osm(epw_file_path, standard_to_be_used, ddy_file = nil) @climate_zone = @climate_zone_ashrae # for now we use the california climate zone if it is available @climate_zone = @climate_zone_ca_t24 if !@climate_zone_ca_t24.nil? && standard_to_be_used == CA_TITLE24 + @climate_zone = building.get_climate_zone(standard_to_be_used) if @climate_zone.nil? + OpenStudio.logFree(OpenStudio::Warn, 'BuildingSync.Site.generate_baseline_osm', 'Could not find a climate zone in the BuildingSync file.') if @climate_zone.nil? building.set_weather_and_climate_zone(@climate_zone, epw_file_path, standard_to_be_used, @latitude, @longitude, ddy_file, @weather_file_name, @weather_station_id, @state_name, @city_name) building.generate_baseline_osm(standard_to_be_used) end @@ -298,7 +205,7 @@ def write_parameters_to_xml(ns, site) site.elements["#{ns}:Latitude"].text = @latitude if !@latitude.nil? site.elements["#{ns}:Longitude"].text = @longitude if !@longitude.nil? - write_parameters_to_xml_for_spatial_element(ns, site) + write_parameters_to_xml_for_spatial_element(site, ns) site.elements.each("#{ns}:Buildings/#{ns}:Building") do |buildings_element| @buildings[0].write_parameters_to_xml(ns, buildings_element) diff --git a/lib/buildingsync/model_articulation/spatial_element.rb b/lib/buildingsync/model_articulation/spatial_element.rb index 4b93a4a9..398262fa 100644 --- a/lib/buildingsync/model_articulation/spatial_element.rb +++ b/lib/buildingsync/model_articulation/spatial_element.rb @@ -41,11 +41,11 @@ module BuildingSync # base class for objects that will configure workflows based on building sync files - class SpatialElement - include OsLib_ModelGeneration + ## + # initialize SpatialElement class def initialize @total_floor_area = nil @bldg_type = nil @@ -61,6 +61,12 @@ def initialize @custom_conditioned_below_grade_floor_area = nil end + ## + # read floor areas + ## + # @param build_element [REXML::Element] + # @param parent_total_floor_area [float] + # @param ns [string] def read_floor_areas(build_element, parent_total_floor_area, ns) build_element.elements.each("#{ns}:FloorAreas/#{ns}:FloorArea") do |floor_area_element| next if !floor_area_element.elements["#{ns}:FloorAreaValue"] @@ -127,6 +133,13 @@ def read_floor_areas(build_element, parent_total_floor_area, ns) end end + ## + # read occupancy type + ## + # @param xml_element [REXML::Element] + # @param occupancy_type [string] + # @param ns [string] + # @return [string] def read_occupancy_type(xml_element, occupancy_type, ns) occ_element = xml_element.elements["#{ns}:OccupancyClassification"] if !occ_element.nil? @@ -136,6 +149,12 @@ def read_occupancy_type(xml_element, occupancy_type, ns) end end + ## + # set building and system type + ## + # @param occupancy_type [string] + # @param total_floor_area [float] + # @param raise_exception [boolean] def set_bldg_and_system_type(occupancy_type, total_floor_area, raise_exception) # DOE Prototype building types:from openstudio-standards/lib/openstudio-standards/prototypes/common/prototype_metaprogramming.rb # SmallOffice, MediumOffice, LargeOffice, RetailStandalone, RetailStripmall, PrimarySchool, SecondarySchool, Outpatient @@ -160,6 +179,12 @@ def set_bldg_and_system_type(occupancy_type, total_floor_area, raise_exception) puts "to get @bldg_type #{@bldg_type}, @bar_division_method #{@bar_division_method} and @system_type: #{@system_type}" end + ## + # process building and system type + ## + # @param json [string] + # @param occupancy_type [string] + # @param total_floor_area [float] def process_bldg_and_system_type(json, occupancy_type, total_floor_area) puts "using occupancy_type #{occupancy_type} and total floor area: #{total_floor_area}" min_floor_area_correct = false @@ -201,17 +226,36 @@ def process_bldg_and_system_type(json, occupancy_type, total_floor_area) raise "Occupancy type #{occupancy_type} is not available in the bldg_and_system_types.json dictionary" end + ## + # validate positive number excluding zero + ## + # @param name [string] + # @param value [float] + # @return float def validate_positive_number_excluding_zero(name, value) puts "Error: parameter #{name} must be positive and not zero." if value <= 0 return value end + ## + # validate positive number including zero + ## + # @param name [string] + # @param value [float] + # @return float def validate_positive_number_including_zero(name, value) puts "Error: parameter #{name} must be positive or zero." if value < 0 return value end + ## # create space types + ## + # @param model [OpenStudio::Model] + # @param total_bldg_floor_area [float] + # @param standard_template [string] + # @param open_studio_standard [Standard] + # @return hash def create_space_types(model, total_bldg_floor_area, standard_template, open_studio_standard) # create space types from section type # mapping lookup_name name is needed for a few methods @@ -263,6 +307,13 @@ def create_space_types(model, total_bldg_floor_area, standard_template, open_stu return @space_types_floor_area end + ## + # add user defined field to xml file + ## + # @param user_defined_fields [REXML::Element] + # @param ns [string] + # @param field_name [string] + # @param field_value [string] def add_user_defined_field_to_xml_file(user_defined_fields, ns, field_name, field_value) user_defined_field = REXML::Element.new("#{ns}:UserDefinedField") field_name_element = REXML::Element.new("#{ns}:FieldName") @@ -278,8 +329,12 @@ def add_user_defined_field_to_xml_file(user_defined_fields, ns, field_name, fiel end end - - def write_parameters_to_xml_for_spatial_element(ns, xml_element) + ## + # write parameters to xml for spatial element + ## + # @param ns [string] + # @param xml_element [REXML::Element] + def write_parameters_to_xml_for_spatial_element(xml_element, ns) user_defined_fields = REXML::Element.new("#{ns}:UserDefinedFields") xml_element.add_element(user_defined_fields) @@ -292,6 +347,11 @@ def write_parameters_to_xml_for_spatial_element(ns, xml_element) add_floor_area_field_to_xml_file(xml_element, ns) end + ## + # add floor area field to xml file + ## + # @param xml_element [REXML::Element] + # @param ns [string] def add_floor_area_field_to_xml_file(xml_element, ns) xml_element.elements.each("#{ns}:FloorAreas/#{ns}:FloorArea") do |floor_area_element| next if !floor_area_element.elements["#{ns}:FloorAreaValue"] @@ -321,8 +381,6 @@ def add_floor_area_field_to_xml_file(xml_element, ns) end end - def validate_fraction; end - attr_reader :total_floor_area, :bldg_type, :system_type, :space_types end end diff --git a/lib/buildingsync/translator.rb b/lib/buildingsync/translator.rb index 4d9cbe2b..88fa9dd7 100644 --- a/lib/buildingsync/translator.rb +++ b/lib/buildingsync/translator.rb @@ -304,6 +304,7 @@ def write_parameters_to_xml(xml_file_path = nil) save_xml(xml_file_path) if !xml_file_path.nil? end + # osm file path of the baseline model attr_reader :osm_baseline_path end end diff --git a/spec/files/L000_OpenStudio_Pre-Simulation_02.xml b/spec/files/L000_OpenStudio_Pre-Simulation_02.xml new file mode 100644 index 00000000..d4623fea --- /dev/null +++ b/spec/files/L000_OpenStudio_Pre-Simulation_02.xml @@ -0,0 +1,63 @@ + + + + + + + + + Office Carolina + + + + 6A + + + Commercial + Office + + + Gross + 31053 + + + 1915 + + + + + + + + + Baseline + + + + + + Not Started + + + + + + + + + + + + + + + + + diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index caf64d0f..bc1b603e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -92,9 +92,7 @@ def run_baseline_simulation(osm_name, epw_file_path) extension = OpenStudio::Extension::Extension.new runner_options = { run_simulations: true } runner = OpenStudio::Extension::Runner.new(extension.root_dir, nil, runner_options) - result = runner.run_osw(osw_path, osm_baseline_dir) - puts result - # todo: test all the osw_files for results + runner.run_osw(osw_path, osm_baseline_dir) expect(File.exist?(osw_path.gsub('in.osw', 'eplusout.sql'))).to be true end diff --git a/spec/tests/translator_baseline_generation_spec.rb b/spec/tests/translator_baseline_generation_spec.rb index cb6d415a..d74edd96 100644 --- a/spec/tests/translator_baseline_generation_spec.rb +++ b/spec/tests/translator_baseline_generation_spec.rb @@ -92,4 +92,8 @@ expect(e.message.include?('Error: There is more than one (3) building attached to this site in your BuildingSync file.')).to be true end end + + it 'should parse L000_OpenStudio_Pre-Simulation_02.xml with ASHRAE 90.1, perform a sizing run, and create an in.osm' do + test_baseline_creation('L000_OpenStudio_Pre-Simulation_02.xml', ASHRAE90_1) + end end