From b196954361eac51fb699eeea4b494ab3ee446410 Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Mon, 23 Aug 2021 17:30:14 -0600 Subject: [PATCH 01/20] initial test of using epJSON --- lib/openstudio/workflow/jobs/run_translation.rb | 12 +++++++++++- lib/openstudio/workflow/run.rb | 2 ++ lib/openstudio/workflow/util/energyplus.rb | 17 +++++++++++++---- lib/openstudio/workflow/util/model.rb | 9 +++++++-- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/lib/openstudio/workflow/jobs/run_translation.rb b/lib/openstudio/workflow/jobs/run_translation.rb index d4fc00b..a35d3bb 100644 --- a/lib/openstudio/workflow/jobs/run_translation.rb +++ b/lib/openstudio/workflow/jobs/run_translation.rb @@ -66,10 +66,20 @@ def perform @logger.warn "EPW file not found or not sent to #{self.class}" end + # Translate the OSM to an epJSON if true + if @options[:epjson] + @logger.info 'Beginning the translation to epJSON' + @registry[:time_logger]&.start('Translating to EnergyPlus') + model_epjson = translate_to_energyplus @registry[:model], @logger, @options[:epjson] + @registry[:time_logger]&.stop('Translating to EnergyPlus') + @registry.register(:model_epjson) { model_epjson } + @logger.info 'Successfully translated to epJSON' + end + # Translate the OSM to an IDF @logger.info 'Beginning the translation to IDF' @registry[:time_logger]&.start('Translating to EnergyPlus') - model_idf = translate_to_energyplus @registry[:model], @logger + model_idf = translate_to_energyplus @registry[:model], @logger, false @registry[:time_logger]&.stop('Translating to EnergyPlus') @registry.register(:model_idf) { model_idf } @logger.info 'Successfully translated to IDF' diff --git a/lib/openstudio/workflow/run.rb b/lib/openstudio/workflow/run.rb index efe7435..2e8a6a7 100644 --- a/lib/openstudio/workflow/run.rb +++ b/lib/openstudio/workflow/run.rb @@ -95,6 +95,7 @@ def self.default_jobs # @option user_options [Hash] :targets Log targets to write to, defaults to standard out and run.log # @option user_options [Hash] :verify_osw Check OSW for correctness, defaults to true # @option user_options [Hash] :weather_file Initial weather file to load, overrides OSW option if set, defaults to empty + # @option user_options [Hash] :epjson - Create, export and run using epjson format. Default is IDF def initialize(osw_path, user_options = {}) # DLM - what is final_message? @final_message = '' @@ -199,6 +200,7 @@ def initialize(osw_path, user_options = {}) @options[:weather_file] = @input_adapter.weather_file(user_options, nil) @options[:fast] = @input_adapter.fast(user_options, false) @options[:skip_zip_results] = @input_adapter.skip_zip_results(user_options, false) + @options[:epjson] = @input_adapter.epjson(user_options, false) openstudio_dir = 'unknown' begin diff --git a/lib/openstudio/workflow/util/energyplus.rb b/lib/openstudio/workflow/util/energyplus.rb index b2d5910..9207bd4 100644 --- a/lib/openstudio/workflow/util/energyplus.rb +++ b/lib/openstudio/workflow/util/energyplus.rb @@ -95,10 +95,19 @@ def prepare_energyplus_dir(run_directory, logger, energyplus_path = nil) next if File.directory? file # copy idd and ini files - if File.extname(file).downcase =~ /.idd|.ini/ - dest_file = "#{run_directory}/#{File.basename(file)}" - energyplus_files << dest_file - FileUtils.copy file, dest_file + + if @options[:epjson] + if File.extname(file).downcase =~ /.epjson/ + dest_file = "#{run_directory}/#{File.basename(file)}" + energyplus_files << dest_file + FileUtils.copy file, dest_file + end + else + if File.extname(file).downcase =~ /.idd|.ini/ + dest_file = "#{run_directory}/#{File.basename(file)}" + energyplus_files << dest_file + FileUtils.copy file, dest_file + end end energyplus_exe = file if File.basename(file) =~ ENERGYPLUS_REGEX diff --git a/lib/openstudio/workflow/util/model.rb b/lib/openstudio/workflow/util/model.rb index a40f4c1..bd85af8 100644 --- a/lib/openstudio/workflow/util/model.rb +++ b/lib/openstudio/workflow/util/model.rb @@ -94,7 +94,7 @@ def load_idf(idf_path, logger) # @return [Object] Returns and OpenStudio::Workspace object # @todo (rhorsey) rescue errors here # - def translate_to_energyplus(model, logger = nil) + def translate_to_energyplus(model, logger = nil, epjson = false) logger ||= ::Logger.new($stdout) logger.info 'Translate object to EnergyPlus IDF in preparation for EnergyPlus' a = ::Time.now @@ -105,7 +105,12 @@ def translate_to_energyplus(model, logger = nil) model_idf = forward_translator.translateModel(model) b = ::Time.now logger.info "Translate object to EnergyPlus IDF took #{b.to_f - a.to_f}" - model_idf + if epjson + model_epjson = openstudio::epJSON::toJSON(model_idf) + return model_epjson + else + return model_idf + end end # Saves an OpenStudio model object to file From ad57a51a335e15ca6ae46e613d7c46f3e9e4125a Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Wed, 25 Aug 2021 11:22:32 -0600 Subject: [PATCH 02/20] add methods to support epJSON workflow if enabled. Use IDF as default= --- .../workflow/adapters/input/local.rb | 7 ++ .../workflow/jobs/run_translation.rb | 12 +-- lib/openstudio/workflow/util/energyplus.rb | 76 +++++++++++++------ lib/openstudio/workflow/util/model.rb | 46 +++++++++-- lib/openstudio/workflow/version.rb | 2 +- 5 files changed, 101 insertions(+), 42 deletions(-) diff --git a/lib/openstudio/workflow/adapters/input/local.rb b/lib/openstudio/workflow/adapters/input/local.rb index 4f34671..0f2e50a 100644 --- a/lib/openstudio/workflow/adapters/input/local.rb +++ b/lib/openstudio/workflow/adapters/input/local.rb @@ -291,6 +291,13 @@ def verify_osw(user_options, default) return default end + def epjson(user_options, default) + # user option trumps all others + return user_options[:epjson] if user_options[:epjson] + + return default + end + def weather_file(user_options, default) # user option trumps all others return user_options[:weather_file] if user_options[:weather_file] diff --git a/lib/openstudio/workflow/jobs/run_translation.rb b/lib/openstudio/workflow/jobs/run_translation.rb index a35d3bb..d4fc00b 100644 --- a/lib/openstudio/workflow/jobs/run_translation.rb +++ b/lib/openstudio/workflow/jobs/run_translation.rb @@ -66,20 +66,10 @@ def perform @logger.warn "EPW file not found or not sent to #{self.class}" end - # Translate the OSM to an epJSON if true - if @options[:epjson] - @logger.info 'Beginning the translation to epJSON' - @registry[:time_logger]&.start('Translating to EnergyPlus') - model_epjson = translate_to_energyplus @registry[:model], @logger, @options[:epjson] - @registry[:time_logger]&.stop('Translating to EnergyPlus') - @registry.register(:model_epjson) { model_epjson } - @logger.info 'Successfully translated to epJSON' - end - # Translate the OSM to an IDF @logger.info 'Beginning the translation to IDF' @registry[:time_logger]&.start('Translating to EnergyPlus') - model_idf = translate_to_energyplus @registry[:model], @logger, false + model_idf = translate_to_energyplus @registry[:model], @logger @registry[:time_logger]&.stop('Translating to EnergyPlus') @registry.register(:model_idf) { model_idf } @logger.info 'Successfully translated to IDF' diff --git a/lib/openstudio/workflow/util/energyplus.rb b/lib/openstudio/workflow/util/energyplus.rb index 9207bd4..e8115a4 100644 --- a/lib/openstudio/workflow/util/energyplus.rb +++ b/lib/openstudio/workflow/util/energyplus.rb @@ -43,6 +43,8 @@ module Util module EnergyPlus require 'openstudio/workflow/util/io' include OpenStudio::Workflow::Util::IO + require 'openstudio/workflow/util/model' + include OpenStudio::Workflow::Util::Model ENERGYPLUS_REGEX = /^energyplus\D{0,4}$/i.freeze EXPAND_OBJECTS_REGEX = /^expandobjects\D{0,4}$/i.freeze @@ -94,20 +96,11 @@ def prepare_energyplus_dir(run_directory, logger, energyplus_path = nil) Dir["#{energyplus_path}/*"].each do |file| next if File.directory? file - # copy idd and ini files - - if @options[:epjson] - if File.extname(file).downcase =~ /.epjson/ - dest_file = "#{run_directory}/#{File.basename(file)}" - energyplus_files << dest_file - FileUtils.copy file, dest_file - end - else - if File.extname(file).downcase =~ /.idd|.ini/ - dest_file = "#{run_directory}/#{File.basename(file)}" - energyplus_files << dest_file - FileUtils.copy file, dest_file - end + # copy idd, ini and epJSON schema files + if File.extname(file).downcase =~ /.idd|.ini|.epjson/ + dest_file = "#{run_directory}/#{File.basename(file)}" + energyplus_files << dest_file + FileUtils.copy file, dest_file end energyplus_exe = file if File.basename(file) =~ ENERGYPLUS_REGEX @@ -139,7 +132,29 @@ def call_energyplus(run_directory, energyplus_path = nil, output_adapter = nil, current_dir = Dir.pwd energyplus_path ||= find_energyplus logger.info "EnergyPlus path is #{energyplus_path}" + + # Translate the IDF to an epJSON if @options[:epjson] is true + # Ideally, this would be done sooner in the workflow process but many processes + # manipulate the model_idf, some which are ep_measures that may not work with json + # so we do the conversion here from idf to epJSON before running energyplus + if @options[:epjson] + @logger.info 'Beginning the translation to epJSON' + @registry[:time_logger]&.start('Translating to EnergyPlus epJSON') + model_epjson = translate_idf_to_epjson @registry[:model_idf], @logger + @registry[:time_logger]&.stop('Translating to EnergyPlus') + @registry.register(:model_epjson) { model_epjson } + @logger.info 'Successfully translated to epJSON' + + @registry[:time_logger]&.start('Saving epJSON') + epjson_name = save_epjson(@registry[:model_epjson], @registry[:root_dir]) + @registry[:time_logger]&.stop('Saving epJSON') + @logger.debug "Saved epJSON as #{epjson_name}" + + end + energyplus_files, energyplus_exe, expand_objects_exe = prepare_energyplus_dir(run_directory, logger, energyplus_path) + + Dir.chdir(run_directory) logger.info "Starting simulation in run directory: #{Dir.pwd}" @@ -161,18 +176,33 @@ def call_energyplus(run_directory, energyplus_path = nil, output_adapter = nil, end end - # create stdout - command = popen_command("\"#{energyplus_exe}\" 2>&1") - logger.info "Running command '#{command}'" - File.open('stdout-energyplus', 'w') do |file| - ::IO.popen(command) do |io| - while (line = io.gets) - file << line - output_adapter&.communicate_energyplus_stdout(line) + # Run using epJSON if @options[:epjson] true, otherwise default + # to use IDF + if @options[:epjson] + command = popen_command("\"#{energyplus_exe} in.epJSON\" 2>&1") + logger.info "Running command '#{command}'" + File.open('stdout-energyplus', 'w') do |file| + ::IO.popen(command) do |io| + while (line = io.gets) + file << line + output_adapter&.communicate_energyplus_stdout(line) + end + end + end + r = $? + else + command = popen_command("\"#{energyplus_exe}\" 2>&1") + logger.info "Running command '#{command}'" + File.open('stdout-energyplus', 'w') do |file| + ::IO.popen(command) do |io| + while (line = io.gets) + file << line + output_adapter&.communicate_energyplus_stdout(line) + end end end + r = $? end - r = $? logger.info "EnergyPlus returned '#{r}'" unless r.to_i.zero? diff --git a/lib/openstudio/workflow/util/model.rb b/lib/openstudio/workflow/util/model.rb index bd85af8..2a82aaf 100644 --- a/lib/openstudio/workflow/util/model.rb +++ b/lib/openstudio/workflow/util/model.rb @@ -94,7 +94,7 @@ def load_idf(idf_path, logger) # @return [Object] Returns and OpenStudio::Workspace object # @todo (rhorsey) rescue errors here # - def translate_to_energyplus(model, logger = nil, epjson = false) + def translate_to_energyplus(model, logger = nil) logger ||= ::Logger.new($stdout) logger.info 'Translate object to EnergyPlus IDF in preparation for EnergyPlus' a = ::Time.now @@ -105,12 +105,23 @@ def translate_to_energyplus(model, logger = nil, epjson = false) model_idf = forward_translator.translateModel(model) b = ::Time.now logger.info "Translate object to EnergyPlus IDF took #{b.to_f - a.to_f}" - if epjson - model_epjson = openstudio::epJSON::toJSON(model_idf) - return model_epjson - else - return model_idf - end + model_idf + end + # Translates an IDF model into an EnergyPlus epJSON object + # + # @param [Object] OpenStudio::IdfFile instance to translate into an OpenStudio epJSON object -- see + # the OpenStudio SDK for details on the process + # @return [Object] Returns and OpenStudio::epJSONobject + # + def translate_idf_to_epjson(model_idf, logger = nil) + logger ||= ::Logger.new($stdout) + logger.info 'Translate IDF to epJSON in preparation for EnergyPlus' + a = ::Time.now + model_epjson = OpenStudio::EPJSON::toJSON(model_idf) + b = ::Time.now + logger.info "Translate IDF to EnergyPlus epJSON took #{b.to_f - a.to_f}" + + model_epjson end # Saves an OpenStudio model object to file @@ -154,6 +165,27 @@ def save_idf(model_idf, save_directory, name = 'in.idf') end idf_filename end + + # Saves an OpenStudio EpJSON model object to file + # + # @param [Object] model The OpenStudio::Workspace instance to save to file + # @param [String] save_directory Folder to save the model in + # @param [String] name ('in.epJSON') Option to define a non-standard name + # @return [String] epJSON file name + # + def save_epjson(model_epjson, save_directory, name = 'in.epJSON') + epjson_filename = File.join(save_directory.to_s, name.to_s) + File.open(epjson_filename, 'w') do |f| + f << model_epjson.to_s + # make sure data is written to the disk one way or the other + begin + f.fsync + rescue StandardError + f.flush + end + end + epjson_filename + end end end end diff --git a/lib/openstudio/workflow/version.rb b/lib/openstudio/workflow/version.rb index f3db2c9..8972164 100644 --- a/lib/openstudio/workflow/version.rb +++ b/lib/openstudio/workflow/version.rb @@ -37,6 +37,6 @@ module OpenStudio module Workflow - VERSION = '2.2.1' # Suffixes must have periods (not dashes) + VERSION = '2.2.2' # Suffixes must have periods (not dashes) end end From de631cb85849d71ae27cdc9f95938b7b36924583 Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Wed, 25 Aug 2021 18:29:52 -0600 Subject: [PATCH 03/20] change method to use toString vs hash --- lib/openstudio/workflow/util/model.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/openstudio/workflow/util/model.rb b/lib/openstudio/workflow/util/model.rb index 2a82aaf..802fc0d 100644 --- a/lib/openstudio/workflow/util/model.rb +++ b/lib/openstudio/workflow/util/model.rb @@ -117,7 +117,7 @@ def translate_idf_to_epjson(model_idf, logger = nil) logger ||= ::Logger.new($stdout) logger.info 'Translate IDF to epJSON in preparation for EnergyPlus' a = ::Time.now - model_epjson = OpenStudio::EPJSON::toJSON(model_idf) + model_epjson = OpenStudio::EPJSON::toJSONString(model_idf) b = ::Time.now logger.info "Translate IDF to EnergyPlus epJSON took #{b.to_f - a.to_f}" From f5c0b182453fe917cfb14ea09be3670617dd0320 Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Thu, 26 Aug 2021 11:15:58 -0600 Subject: [PATCH 04/20] load idf after Expand Objects --- lib/openstudio/workflow/util/energyplus.rb | 44 +++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/openstudio/workflow/util/energyplus.rb b/lib/openstudio/workflow/util/energyplus.rb index e8115a4..0d1a5ba 100644 --- a/lib/openstudio/workflow/util/energyplus.rb +++ b/lib/openstudio/workflow/util/energyplus.rb @@ -133,28 +133,8 @@ def call_energyplus(run_directory, energyplus_path = nil, output_adapter = nil, energyplus_path ||= find_energyplus logger.info "EnergyPlus path is #{energyplus_path}" - # Translate the IDF to an epJSON if @options[:epjson] is true - # Ideally, this would be done sooner in the workflow process but many processes - # manipulate the model_idf, some which are ep_measures that may not work with json - # so we do the conversion here from idf to epJSON before running energyplus - if @options[:epjson] - @logger.info 'Beginning the translation to epJSON' - @registry[:time_logger]&.start('Translating to EnergyPlus epJSON') - model_epjson = translate_idf_to_epjson @registry[:model_idf], @logger - @registry[:time_logger]&.stop('Translating to EnergyPlus') - @registry.register(:model_epjson) { model_epjson } - @logger.info 'Successfully translated to epJSON' - - @registry[:time_logger]&.start('Saving epJSON') - epjson_name = save_epjson(@registry[:model_epjson], @registry[:root_dir]) - @registry[:time_logger]&.stop('Saving epJSON') - @logger.debug "Saved epJSON as #{epjson_name}" - - end - energyplus_files, energyplus_exe, expand_objects_exe = prepare_energyplus_dir(run_directory, logger, energyplus_path) - Dir.chdir(run_directory) logger.info "Starting simulation in run directory: #{Dir.pwd}" @@ -176,8 +156,28 @@ def call_energyplus(run_directory, energyplus_path = nil, output_adapter = nil, end end - # Run using epJSON if @options[:epjson] true, otherwise default - # to use IDF + # Translate the IDF to an epJSON if @options[:epjson] is true + # Ideally, this would be done sooner in the workflow process but many processes + # manipulate the model_idf, some which are ep_measures that may not work with json + # and ExpandObjects does not currently support epjson anyway to that still needs to run + # before this can be changed. + if @options[:epjson] + @logger.info 'Beginning the translation to epJSON' + @registry[:time_logger]&.start('Translating to EnergyPlus epJSON') + idf_final = load_idf('in.idf', @logger) + #model_epjson = translate_idf_to_epjson idf_final, @logger + model_epjson = translate_idf_to_epjson @registry[:model_idf], @logger + @registry[:time_logger]&.stop('Translating to EnergyPlus') + @registry.register(:model_epjson) { model_epjson } + @logger.info 'Successfully translated to epJSON' + @registry[:time_logger]&.start('Saving epJSON') + epjson_name = save_epjson(@registry[:model_epjson], run_directory) + epjson_name = save_epjson(@registry[:model_epjson], @registry[:root_dir]) + @registry[:time_logger]&.stop('Saving epJSON') + @logger.debug "Saved epJSON as #{epjson_name}" + end + + # Run using epJSON if @options[:epjson] true, otherwise use ID if @options[:epjson] command = popen_command("\"#{energyplus_exe} in.epJSON\" 2>&1") logger.info "Running command '#{command}'" From 63c4ba38ed96992711e7c5b6152f270c87b72b7d Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Thu, 26 Aug 2021 16:19:01 -0600 Subject: [PATCH 05/20] clean up e+ calls --- lib/openstudio/workflow/util/energyplus.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/openstudio/workflow/util/energyplus.rb b/lib/openstudio/workflow/util/energyplus.rb index 0d1a5ba..7f19401 100644 --- a/lib/openstudio/workflow/util/energyplus.rb +++ b/lib/openstudio/workflow/util/energyplus.rb @@ -165,21 +165,19 @@ def call_energyplus(run_directory, energyplus_path = nil, output_adapter = nil, @logger.info 'Beginning the translation to epJSON' @registry[:time_logger]&.start('Translating to EnergyPlus epJSON') idf_final = load_idf('in.idf', @logger) - #model_epjson = translate_idf_to_epjson idf_final, @logger - model_epjson = translate_idf_to_epjson @registry[:model_idf], @logger + model_epjson = translate_idf_to_epjson idf_final, @logger @registry[:time_logger]&.stop('Translating to EnergyPlus') @registry.register(:model_epjson) { model_epjson } @logger.info 'Successfully translated to epJSON' @registry[:time_logger]&.start('Saving epJSON') epjson_name = save_epjson(@registry[:model_epjson], run_directory) - epjson_name = save_epjson(@registry[:model_epjson], @registry[:root_dir]) @registry[:time_logger]&.stop('Saving epJSON') @logger.debug "Saved epJSON as #{epjson_name}" end # Run using epJSON if @options[:epjson] true, otherwise use ID if @options[:epjson] - command = popen_command("\"#{energyplus_exe} in.epJSON\" 2>&1") + command = popen_command("\"#{energyplus_exe}\" in.epJSON 2>&1") logger.info "Running command '#{command}'" File.open('stdout-energyplus', 'w') do |file| ::IO.popen(command) do |io| From 6e45501b83431f8705e9c5571bbb44c9660860a1 Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Thu, 26 Aug 2021 16:31:23 -0600 Subject: [PATCH 06/20] clone the workflow_analysis_spec and create same test cases using epjson --- .../workflow/workflow_analysis_epjson_spec.rb | 1262 +++++++++++++++++ 1 file changed, 1262 insertions(+) create mode 100644 spec/openstudio/workflow/workflow_analysis_epjson_spec.rb diff --git a/spec/openstudio/workflow/workflow_analysis_epjson_spec.rb b/spec/openstudio/workflow/workflow_analysis_epjson_spec.rb new file mode 100644 index 0000000..77bd544 --- /dev/null +++ b/spec/openstudio/workflow/workflow_analysis_epjson_spec.rb @@ -0,0 +1,1262 @@ +# frozen_string_literal: true + +# ******************************************************************************* +# OpenStudio(R), Copyright (c) 2008-2021, 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 HOLDERS AND 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, THE UNITED STATES +# GOVERNMENT, OR ANY CONTRIBUTORS 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. +# ******************************************************************************* + +require_relative './../../spec_helper' +require 'json-schema' + +describe 'OSW Integration' do + it 'should run empty OSW file' do + osw_path = File.join(__FILE__, './../../../files/empty_seed_osw/empty.osw') + run_options = { + debug: true, + epjson: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + end + + it 'should run compact OSW file' do + osw_path = File.expand_path('../../files/compact_osw/compact.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + debug: true, + epjson: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to_not be_nil + end + end + + it 'should run compact OSW file in m and w and p mode' do + osw_path = File.expand_path('../../files/compact_mwp_osw/compact_mwp.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + osw_out_m_path = osw_path.gsub(File.basename(osw_path), 'out_m.osw') + osw_out_w_path = osw_path.gsub(File.basename(osw_path), 'out_w.osw') + osw_out_p_path = osw_path.gsub(File.basename(osw_path), 'out_p.osw') + data_point_out_path = osw_path.gsub(File.basename(osw_path), 'run/data_point_out.json') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + FileUtils.rm_rf(osw_out_m_path) if File.exist?(osw_out_m_path) + expect(File.exist?(osw_out_m_path)).to eq false + FileUtils.rm_rf(osw_out_w_path) if File.exist?(osw_out_w_path) + expect(File.exist?(osw_out_w_path)).to eq false + FileUtils.rm_rf(osw_out_p_path) if File.exist?(osw_out_p_path) + expect(File.exist?(osw_out_p_path)).to eq false + FileUtils.rm_rf(data_point_out_path) if File.exist?(data_point_out_path) + expect(File.exist?(data_point_out_path)).to eq false + + # run measures only + run_options = { + debug: true, + epjson: true, + jobs: [ + { state: :queued, next_state: :initialization, options: { initial: true } }, + { state: :initialization, next_state: :os_measures, job: :RunInitialization, + file: 'openstudio/workflow/jobs/run_initialization.rb', options: {} }, + { state: :os_measures, next_state: :translator, job: :RunOpenStudioMeasures, + file: 'openstudio/workflow/jobs/run_os_measures.rb', options: {} }, + { state: :translator, next_state: :ep_measures, job: :RunTranslation, + file: 'openstudio/workflow/jobs/run_translation.rb', options: {} }, + { state: :ep_measures, next_state: :finished, job: :RunEnergyPlusMeasures, + file: 'openstudio/workflow/jobs/run_ep_measures.rb', options: {} }, + { state: :postprocess, next_state: :finished, job: :RunPostprocess, + file: 'openstudio/workflow/jobs/run_postprocess.rb', options: {} }, + { state: :finished }, + { state: :errored } + ] + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + FileUtils.cp(osw_out_path, osw_out_m_path) + + # DLM: TODO, the following line fails currently because the results hash is only populated in run_reporting_measures + # with a call to run_extract_inputs_and_outputs, seems like this should be called after running model and e+ measures + # expect(File.exist?(data_point_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to eq 4 + expect(osw_out[:steps][0][:result]).to_not be_nil + expect(osw_out[:steps][0][:result][:step_initial_condition]).to be_nil + expect(osw_out[:steps][0][:result][:step_result]).to eq 'Skip' + expect(osw_out[:steps][1][:result]).to_not be_nil + expect(osw_out[:steps][1][:result][:step_initial_condition]).to eq 'IncreaseInsulationRValueForRoofsByPercentage' + expect(osw_out[:steps][1][:result][:step_result]).to eq 'Success' + expect(osw_out[:steps][2][:result]).to_not be_nil + expect(osw_out[:steps][2][:result][:step_initial_condition]).to eq 'SetEnergyPlusInfiltrationFlowRatePerFloorArea' + expect(osw_out[:steps][2][:result][:step_result]).to eq 'Success' + expect(osw_out[:steps][3][:result]).to be_nil + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + FileUtils.rm_rf(data_point_out_path) if File.exist?(data_point_out_path) + expect(File.exist?(data_point_out_path)).to eq false + + # run + run_options = { + debug: true, + epjson: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + FileUtils.cp(osw_out_path, osw_out_w_path) + + expect(File.exist?(data_point_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to eq 4 + expect(osw_out[:steps][0][:result]).to_not be_nil + expect(osw_out[:steps][0][:result][:step_initial_condition]).to be_nil + expect(osw_out[:steps][0][:result][:step_result]).to eq 'Skip' + expect(osw_out[:steps][1][:result]).to_not be_nil + expect(osw_out[:steps][1][:result][:step_initial_condition]).to eq 'IncreaseInsulationRValueForRoofsByPercentage' + expect(osw_out[:steps][1][:result][:step_result]).to eq 'Success' + expect(osw_out[:steps][2][:result]).to_not be_nil + expect(osw_out[:steps][2][:result][:step_initial_condition]).to eq 'SetEnergyPlusInfiltrationFlowRatePerFloorArea' + expect(osw_out[:steps][2][:result][:step_result]).to eq 'Success' + expect(osw_out[:steps][3][:result]).to_not be_nil + expect(osw_out[:steps][3][:result][:step_initial_condition]).to eq 'DencityReports' + expect(osw_out[:steps][3][:result][:step_result]).to eq 'Success' + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + FileUtils.rm_rf(data_point_out_path) if File.exist?(data_point_out_path) + expect(File.exist?(data_point_out_path)).to eq false + + # run post process + run_options = { + debug: true, + epjson: true, + preserve_run_dir: true, + jobs: [ + { state: :queued, next_state: :initialization, options: { initial: true } }, + { state: :initialization, next_state: :reporting_measures, job: :RunInitialization, + file: 'openstudio/workflow/jobs/run_initialization.rb', options: {} }, + { state: :reporting_measures, next_state: :postprocess, job: :RunReportingMeasures, + file: 'openstudio/workflow/jobs/run_reporting_measures.rb', options: {} }, + { state: :postprocess, next_state: :finished, job: :RunPostprocess, + file: 'openstudio/workflow/jobs/run_postprocess.rb', options: {} }, + { state: :finished }, + { state: :errored } + ] + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + FileUtils.cp(osw_out_path, osw_out_p_path) + + expect(File.exist?(data_point_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps][0][:result]).to be_nil + expect(osw_out[:steps][1][:result]).to be_nil + expect(osw_out[:steps][2][:result]).to be_nil + expect(osw_out[:steps][3][:result]).to_not be_nil + expect(osw_out[:steps][3][:result][:step_initial_condition]).to eq 'DencityReports' + expect(osw_out[:steps][3][:result][:step_result]).to eq 'Success' + expect(osw_out[:steps][3][:result][:step_final_condition]).to eq 'DEnCity Report generated successfully.' + end + + it 'should run an extended OSW file' do + osw_path = File.expand_path('../../files/extended_osw/example/workflows/extended.osw', __dir__) + run_options = { + debug: true, + epjson: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + end + + it 'should run an alternate path OSW file' do + osw_path = File.expand_path('../../files/alternate_paths/osw_and_stuff/in.osw', __dir__) + run_options = { + debug: true, + epjson: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + end + + it 'should run OSW file with skips' do + osw_path = File.expand_path('../../files/skip_osw/skip.osw', __dir__) + run_options = { + debug: true, + epjson: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + end + + it 'should run OSW file with handle arguments' do + osw_path = File.expand_path('../../files/handle_args_osw/handle_args.osw', __dir__) + run_options = { + debug: true, + epjson: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + end + + it 'should run OSW with output requests file' do + osw_path = File.expand_path('../../files/output_request_osw/output_request.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + debug: true, + epjson: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to_not be_nil + end + + idf_out_path = osw_path.gsub(File.basename(osw_path), 'in.idf') + + expect(File.exist?(idf_out_path)).to eq true + + workspace = OpenStudio::Workspace.load(idf_out_path) + expect(workspace.empty?).to eq false + + workspace = workspace.get + + targets = {} + targets['Electricity:Facility'] = false + targets['Gas:Facility'] = false + targets['District Cooling Chilled Water Rate'] = false + targets['District Cooling Mass Flow Rate'] = false + targets['District Cooling Inlet Temperature'] = false + targets['District Cooling Outlet Temperature'] = false + targets['District Heating Hot Water Rate'] = false + targets['District Heating Mass Flow Rate'] = false + targets['District Heating Inlet Temperature'] = false + targets['District Heating Outlet Temperature'] = false + + workspace.getObjectsByType('Output:Variable'.to_IddObjectType).each do |object| + name = object.getString(1) + expect(name.empty?).to eq false + name = name.get + targets[name] = true + end + + targets.each_key do |key| + expect(targets[key]).to eq true + end + + # make sure that the reports exist + report_filename = File.join(File.dirname(osw_path), 'reports', 'dencity_reports_report_timeseries.csv') + expect(File.exist?(report_filename)).to eq true + report_filename = File.join(File.dirname(osw_path), 'reports', 'openstudio_results_report.html') + expect(File.exist?(report_filename)).to eq true + report_filename = File.join(File.dirname(osw_path), 'reports', 'eplustbl.html') + expect(File.exist?(report_filename)).to eq true + end + + it 'should run OSW file with web adapter' do + require 'openstudio/workflow/adapters/output/web' + + osw_path = File.expand_path('../../files/web_osw/web.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + run_dir = File.join(File.dirname(osw_path), 'run') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + output_adapter = OpenStudio::Workflow::OutputAdapter::Web.new(output_directory: run_dir, url: 'http://www.example.com') + + run_options = { + debug: true, + epjson: true, + output_adapter: output_adapter + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to_not be_nil + end + end + + it 'should run OSW file with socket adapter' do + require 'openstudio/workflow/adapters/output/socket' + + osw_path = File.expand_path('../../files/socket_osw/socket.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + run_dir = File.join(File.dirname(osw_path), 'run') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + port = 2000 + content = '' + + server = TCPServer.open('localhost', port) + t = Thread.new do + while client = server.accept + while line = client.gets + content += line + end + end + end + + output_adapter = OpenStudio::Workflow::OutputAdapter::Socket.new(output_directory: run_dir, port: port) + + run_options = { + debug: true, + epjson: true, + output_adapter: output_adapter + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + Thread.kill(t) + + # puts "content = #{content}" + + expect(content).to match(/Applying IncreaseInsulationRValueForExteriorWallsByPercentage/) + expect(content).to match(/For construction'EXTERIOR-WALL adj exterior wall insulation', material'Wood-Framed - 4 in. Studs - 16 in. OC - R-11 Cavity Insulation_R-value 30.0% increase' was altered./) + expect(content).to match(/Applied IncreaseInsulationRValueForExteriorWallsByPercentage/) + expect(content).to match(/Applying IncreaseInsulationRValueForRoofsByPercentage/) + expect(content).to match(/The building had 1 roof constructions: EXTERIOR-ROOF \(R-31\.2\)/) + expect(content).to match(/Applied IncreaseInsulationRValueForRoofsByPercentage/) + expect(content).to match(/Applying SetEnergyPlusInfiltrationFlowRatePerFloorArea/) + expect(content).to match(/The building finished with flow per zone floor area values ranging from 10\.76 to 10\.76/) + expect(content).to match(/Applied SetEnergyPlusInfiltrationFlowRatePerFloorArea/) + expect(content).to match(/Starting state initialization/) + # expect(content).to match(/Processing Data Dictionary/) + # expect(content).to match(/Writing final SQL reports/) + expect(content).to match(/Applying DencityReports/) + expect(content).to match(/DEnCity Report generated successfully/) + expect(content).to match(/Saving Dencity metadata csv file/) + expect(content).to match(/Applied DencityReports/) + expect(content).to match(/Complete/) + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to_not be_nil + end + end + + it 'should run OSW file with no epw file' do + osw_path = File.expand_path('../../files/no_epw_file_osw/no_epw_file.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + run_dir = File.join(File.dirname(osw_path), 'run') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + debug: true, + epjson: true + + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to_not be_nil + end + end + + it 'should run OSW file in measure only mode' do + osw_path = File.expand_path('../../files/measures_only_osw/measures_only.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + idf_out_path = osw_path.gsub(File.basename(osw_path), 'run/in.idf') + osm_out_path = osw_path.gsub(File.basename(osw_path), 'run/in.osm') + run_dir = File.join(File.dirname(osw_path), 'run') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + FileUtils.rm_rf(idf_out_path) if File.exist?(idf_out_path) + expect(File.exist?(idf_out_path)).to eq false + + FileUtils.rm_rf(osm_out_path) if File.exist?(osm_out_path) + expect(File.exist?(osm_out_path)).to eq false + + run_options = {} + # run_options = { + # debug: true + # } + run_options[:jobs] = [ + { state: :queued, next_state: :initialization, options: { initial: true } }, + { state: :initialization, next_state: :os_measures, job: :RunInitialization, + file: 'openstudio/workflow/jobs/run_initialization.rb', options: {} }, + { state: :os_measures, next_state: :translator, job: :RunOpenStudioMeasures, + file: 'openstudio/workflow/jobs/run_os_measures.rb', options: {} }, + { state: :translator, next_state: :ep_measures, job: :RunTranslation, + file: 'openstudio/workflow/jobs/run_translation.rb', options: {} }, + { state: :ep_measures, next_state: :preprocess, job: :RunEnergyPlusMeasures, + file: 'openstudio/workflow/jobs/run_ep_measures.rb', options: {} }, + { state: :preprocess, next_state: :postprocess, job: :RunPreprocess, + file: 'openstudio/workflow/jobs/run_preprocess.rb', options: {} }, + { state: :postprocess, next_state: :finished, job: :RunPostprocess, + file: 'openstudio/workflow/jobs/run_postprocess.rb', options: {} }, + { state: :finished }, + { state: :errored } + ] + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + expect(File.exist?(idf_out_path)).to eq true + expect(File.exist?(osm_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to_not be_nil + end + end + + it 'should run OSW with display name or value for choice arguments' do + osw_path = File.expand_path('../../files/value_or_displayname_choice_osw/value_or_displayname_choice.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + debug: true, + epjson: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to_not be_nil + end + end + + # XcelEDAReportingandQAQC measure not work with OS 3.1 + # it 'should error out nicely' do + # osw_path = File.expand_path('../../files/reporting_measure_error/reporting_measure_error.osw', __dir__) + # osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + # FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + # expect(File.exist?(osw_out_path)).to eq false + + # run_options = { + # debug: true + # } + # k = OpenStudio::Workflow::Run.new osw_path, run_options + # expect(k).to be_instance_of OpenStudio::Workflow::Run + # expect(k.run).to eq :errored + + # expect(File.exist?(osw_out_path)).to eq true + + # osw_out = nil + # File.open(osw_out_path, 'r') do |file| + # osw_out = JSON.parse(file.read, symbolize_names: true) + # end + + # expect(osw_out).to be_instance_of Hash + # expect(osw_out[:completed_status]).to eq 'Fail' + # expect(osw_out[:steps]).to be_instance_of Array + # expect(osw_out[:steps].size).to be > 0 + # osw_out[:steps].each do |step| + # expect(step[:result]).to_not be_nil + # # Only the EDA reporting measure step is supposed to have failed + # if step[:measure_dir_name] == 'Xcel EDA Reporting and QAQC' + # expect(step[:result][:step_result]).to eq 'Fail' + # else + # expect(step[:result][:step_result]).to eq 'Success' + # end + # end + + # expected_r = /Peak Demand timeseries \(Electricity:Facility at zone timestep\) could not be found, cannot determine the informati(no|on) needed to calculate savings or incentives./ + # expect(osw_out[:steps].last[:result][:step_errors].last).to match expected_r + + # # TODO: Temporary comment + # # Not sure why the in.idf ends up there at the root? Shouldn't it just be + # # in run/in.idf? + # idf_out_path = osw_path.gsub(File.basename(osw_path), 'in.idf') + # expect(File.exist?(idf_out_path)).to eq true + + # # even if it fails, make sure that we save off the datapoint.zip + # # It shouldn't be in the wrong location at root next to OSW + # zip_path = osw_path.gsub(File.basename(osw_path), 'data_point.zip') + # expect(File.exist?(zip_path)).to eq false + # # It should be under run/ + # zip_path = File.join(File.dirname(osw_path), 'run', 'data_point.zip') + # expect(File.exist?(zip_path)).to eq true + # end + + it 'should raise error when full measure directory path specified' do + osw_path = File.expand_path('../../files/full_measure_dir_osw/full_measure_dir.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + epjson: true, + debug: true + } + + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :errored + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Fail' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be == 1 + + expect(osw_out[:steps][0]).to_not be_nil + + if osw_out[:steps][0][:measure_dir_name] == '/OpenStudio-workflow-gem/spec/files/full_measure_dir_osw/measures/' + expect(osw_out[:steps][0][:result][:step_warnings]).to eq 'measure_dir_name should not be a full path. It should be a relative path to the measure directory or the name of the measure directory containing the measure.rb file.' + end + end + + it 'should error out while copying html reports' do + osw_path = File.expand_path('../../files/reporting_measure_raise/reporting_measure_raise.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + epjson: true, + debug: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :errored + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Fail' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to_not be_nil + # Only the broken reporting measure step is supposed to have failed + if step[:measure_dir_name] == 'purposefully_broken_reporting_measure' + expect(step[:result][:step_result]).to eq 'Fail' + else + expect(step[:result][:step_result]).to eq 'Success' + end + end + + expected_r = /I'm purposefully breaking the reporting! Before the report was already created!/ + expect(osw_out[:steps].last[:result][:step_errors].last).to match expected_r + + idf_out_path = File.join(File.dirname(osw_path), 'run', 'in.idf') + expect(File.exist?(idf_out_path)).to eq true + + zip_path = File.join(File.dirname(osw_path), 'run', 'data_point.zip') + expect(File.exist?(zip_path)).to eq true + + # Tests that we find the two reports that actually worked fine: + # eplus + openstudio_results. + # The broken one shouldn't be there since I raise before it + reports_dir_path = File.join(File.dirname(osw_path), 'reports') + html_reports = Dir.glob(File.join(reports_dir_path, '*.html')) + html_reports_names = html_reports.map { |f| File.basename(f) } + expect(html_reports_names.size).to eq 2 + expect(html_reports_names.include?('eplustbl.html')).to eq true + expect(html_reports_names.include?('openstudio_results_report.html')).to eq true + end + + it 'should allow passing model to arguments() method of ReportingMeasure' do + osw_path = File.expand_path('../../files/reporting_measure_arguments_model/reporting_measure_arguments_model.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + epjson: true, + debug: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be 1 + step = osw_out[:steps][0] + expect(step[:measure_dir_name]).to eq 'reporting_measure_that_takes_model_in_arguments' + expect(step[:result][:step_result]).to eq 'Success' + + expect(step[:result][:step_info]).to include('Getting argument add_for_thermal_zones') + expect(step[:result][:step_info]).to include('Argument add_for_thermal_zones is true') + + idf_out_path = File.join(File.dirname(osw_path), 'run', 'in.idf') + expect(File.exist?(idf_out_path)).to eq true + + zip_path = File.join(File.dirname(osw_path), 'run', 'data_point.zip') + expect(File.exist?(zip_path)).to eq true + + # Tests that we find the two reports that actually worked fine: + # eplus + our reporting measure. + reports_dir_path = File.join(File.dirname(osw_path), 'reports') + html_reports = Dir.glob(File.join(reports_dir_path, '*.html')) + html_reports_names = html_reports.map { |f| File.basename(f) } + expect(html_reports_names.size).to eq 2 + expect(html_reports_names.include?('eplustbl.html')).to eq true + expect(html_reports_names.include?('reporting_measure_that_takes_model_in_arguments_report.html')).to eq true + end + + # XcelEDAReportingandQAQC no workie + # it 'should associate results with the correct step' do + # (1..2).each do |i| + # osw_path = File.expand_path("./../../../files/results_in_order/data_point_#{i}/data_point.osw", __FILE__) + # osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + # FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + # expect(File.exist?(osw_out_path)).to eq false + + # if !File.exist?(osw_out_path) + # run_options = { + # debug: true + # } + # k = OpenStudio::Workflow::Run.new osw_path, run_options + # expect(k).to be_instance_of OpenStudio::Workflow::Run + # expect(k.run).to eq :finished + # end + + # expect(File.exist?(osw_out_path)).to eq true + + # osw_out = nil + # File.open(osw_out_path, 'r') do |file| + # osw_out = JSON.parse(file.read, symbolize_names: true) + # end + + # expect(osw_out).to be_instance_of Hash + # expect(osw_out[:completed_status]).to eq 'Success' + # expect(osw_out[:steps]).to be_instance_of Array + # expect(osw_out[:steps].size).to be == 3 + # osw_out[:steps].each do |step| + # expect(step[:arguments]).to_not be_nil + + # arguments = step[:arguments] + # puts "arguments = #{arguments}" + + # expect(step[:result]).to_not be_nil + # expect(step[:result][:step_values]).to_not be_nil + + # step_values = step[:result][:step_values] + # puts "step_values = #{step_values}" + + # # check that each argument is in a value + # skipped = false + # arguments.each_pair do |argument_name, argument_value| + # argument_name = argument_name.to_s + # if argument_name == '__SKIP__' + # skipped = argument_value + # end + # end + + # if skipped + + # # step_values are not populated if the measure is skipped + # expect(step_values.size).to be == 0 + # expect(step[:result][:step_result]).to be == 'Skip' + + # else + + # arguments.each_pair do |argument_name, argument_value| + # argument_name = argument_name.to_s + # next if argument_name == '__SKIP__' + + # puts "argument_name = #{argument_name}" + # puts "argument_value = #{argument_value}" + # i = step_values.find_index { |x| x[:name] == argument_name } + # expect(i).to_not be_nil + # expect(step_values[i][:value]).to be == argument_value + # end + + # expect(step[:result][:step_result]).to be == 'Success' + # end + + # expected_results = [] + # if step[:measure_dir_name] == 'XcelEDAReportingandQAQC' + # expected_results << 'cash_flows_capital_type' + # expected_results << 'annual_consumption_electricity' + # expected_results << 'annual_consumption_gas' + # end + + # expected_results.each do |expected_result| + # i = step_values.find_index { |x| x[:name] == expected_result } + # expect(i).to_not be_nil + # end + # end + # end + # end + + it 'should run OSW custom output adapter' do + osw_path = File.expand_path('../../files/run_options_osw/run_options.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + custom_start_path = File.expand_path('../../files/run_options_osw/run/custom_started.job', __dir__) + FileUtils.rm_rf(custom_start_path) if File.exist?(custom_start_path) + expect(File.exist?(custom_start_path)).to eq false + + custom_finished_path = File.expand_path('../../files/run_options_osw/run/custom_finished.job', __dir__) + FileUtils.rm_rf(custom_finished_path) if File.exist?(custom_finished_path) + expect(File.exist?(custom_finished_path)).to eq false + + run_options = { + epjson: true, + debug: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + begin + OpenStudio::RunOptions.new + expect(File.exist?(custom_start_path)).to eq true + expect(File.exist?(custom_finished_path)).to eq true + rescue NameError => e + # feature not available + end + end + + it 'should handle weather file throughout the run' do + osw_path = File.expand_path('../../files/weather_file/weather_file.osw', __dir__) + expect(File.exist?(osw_path)).to eq true + + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + workflow_json = nil + begin + workflow_json = OpenStudio::WorkflowJSON.new(OpenStudio::Path.new(osw_path)) + rescue NameError => e + workflow = ::JSON.parse(File.read(osw_path), symbolize_names: true) + workflow_json = WorkflowJSON_Shim.new(workflow, File.dirname(osw_path)) + end + + seed = workflow_json.seedFile + expect(seed.empty?).to be false + seed = workflow_json.findFile(seed.get) + expect(seed.empty?).to be false + + vt = OpenStudio::OSVersion::VersionTranslator.new + model = vt.loadModel(seed.get) + expect(model.empty?).to be false + + weather_file = model.get.getOptionalWeatherFile + expect(weather_file.empty?).to be false + weather_file_path = weather_file.get.path + expect(weather_file_path.empty?).to be false + weather_file_path = workflow_json.findFile(weather_file_path.get.to_s) + expect(weather_file_path.empty?).to be false + expect(File.exist?(weather_file_path.get.to_s)).to be true + expect(File.basename(weather_file_path.get.to_s)).to eq 'USA_CO_Golden-NREL.724666_TMY3.epw' + + weather_file_path = workflow_json.weatherFile + expect(weather_file_path.empty?).to be false + weather_file_path = workflow_json.findFile(weather_file_path.get.to_s) + expect(weather_file_path.empty?).to be false + expect(File.exist?(weather_file_path.get.to_s)).to be true + expect(File.basename(weather_file_path.get.to_s)).to eq 'USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw' + + run_options = { + epjson: true, + debug: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + # check epw in run dir + + # check sql + + # add reporting measure to check? + end + + it 'should run null_seed OSW file' do + osw_path = File.expand_path('../../files/null_seed/null_seed.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + epjson: true, + debug: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to_not be_nil + end + end + + it 'should run an initially empty EPW file' do + osw_path = File.expand_path('../../files/empty_epw/empty_epw.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + epjson: true, + debug: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to_not be_nil + end + end + + it 'should fail to run an OSW with out of order steps' do + osw_path = File.expand_path('../../files/bad_order_osw/bad_order.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + epjson: true, + debug: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :errored + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Fail' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to be_nil # cause they did not run + end + end + + it 'should register repeated measure results by name if the name key exists' do + osw_path = File.expand_path('../../files/repeated_measure_osw/repeated_measure.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + out_json_path = File.expand_path('../../files/repeated_measure_osw/run/measure_attributes.json', __dir__) + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + epjson: true, + debug: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to_not be_nil + end + + expect(File.exist?(out_json_path)).to eq true + + attr_json = JSON.parse(File.read(out_json_path), symbolize_names: true) + + expect(attr_json).to be_instance_of Hash + expect(attr_json.keys).to include(:measure_1, :measure_2) + expect(attr_json[:measure_1].keys).to include(:r_value, :applicable) + expect(attr_json[:measure_1][:r_value]).to eq 45 + expect(attr_json[:measure_1][:applicable]).to eq true + expect(attr_json[:measure_2].keys).to include(:r_value, :applicable) + expect(attr_json[:measure_2][:r_value]).to eq 45 + expect(attr_json[:measure_2][:applicable]).to eq true + end + + it 'should test halt_workflow' do + osw_path = File.expand_path('../../files/halt_workflow_osw/halt_workflow.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + epjson: true, + debug: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + + res = k.run + expect(res).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Invalid' + expect(osw_out[:current_step]).to eq 2 + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to eq 4 + expect(osw_out[:steps][0][:result]).to be_instance_of Hash + expect(osw_out[:steps][0][:result][:step_result]).to eq 'Success' + expect(osw_out[:steps][1][:result]).to be_instance_of Hash + expect(osw_out[:steps][1][:result][:step_result]).to eq 'Success' + expect(osw_out[:steps][2][:result]).to be_nil + expect(osw_out[:steps][3][:result]).to be_nil + end + + it 'should test script errors' do + osw_path = File.expand_path('../../files/script_error_osw/script_error.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + epjson: true, + debug: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + + res = k.run + expect(res).to eq :errored + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Fail' + end + + it 'should run fast OSW file' do + osw_path = File.expand_path('../../files/fast_osw/fast.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + epjson: true, + debug: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + # out.osw not saved in fast mode + expect(File.exist?(osw_out_path)).to eq false + end + + it 'should run skip_zip_results OSW file' do + osw_path = File.expand_path('../../files/skip_zip_results_osw/skip_zip_results.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + zip_path = File.join(File.dirname(osw_path), 'run', 'data_point.zip') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + FileUtils.rm_rf(zip_path) if File.exist?(zip_path) + expect(File.exist?(zip_path)).to eq false + + run_options = { + epjson: true, + debug: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + # out.osw saved in skip_zip_results mode + expect(File.exist?(osw_out_path)).to eq true + + # data_point.zip not saved in skip_zip_results mode + expect(File.exist?(zip_path)).to eq false + end + + it 'should run reporting measures for UrbanOpt with no idf' do + osw_path = File.join(__FILE__, './../../../files/urbanopt/data_point.osw') + # run post process + run_options = { + epjson: true, + debug: true, + preserve_run_dir: false, + jobs: [ + { state: :queued, next_state: :initialization, options: { initial: true } }, + { state: :initialization, next_state: :reporting_measures, job: :RunInitialization, + file: 'openstudio/workflow/jobs/run_initialization.rb', options: {} }, + { state: :reporting_measures, next_state: :postprocess, job: :RunReportingMeasures, + file: 'openstudio/workflow/jobs/run_reporting_measures.rb', options: {} }, + { state: :postprocess, next_state: :finished, job: :RunPostprocess, + file: 'openstudio/workflow/jobs/run_postprocess.rb', options: {} }, + { state: :finished }, + { state: :errored } + ] + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + end + + it 'should run reporting measures without UrbanOpt and with no idf with a fail' do + osw_path = File.join(__FILE__, './../../../files/urbanopt/data_point_no_urbanopt.osw') + run_dir = File.join(__FILE__, './../../../files/urbanopt/run') + # osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + FileUtils.rm_rf(run_dir) # if File.exist?(osw_out_path) + # expect(File.exist?(osw_out_path)).to eq false + + # run post process + run_options = { + epjson: true, + debug: true, + preserve_run_dir: false, + jobs: [ + { state: :queued, next_state: :initialization, options: { initial: true } }, + { state: :initialization, next_state: :reporting_measures, job: :RunInitialization, + file: 'openstudio/workflow/jobs/run_initialization.rb', options: {} }, + { state: :reporting_measures, next_state: :postprocess, job: :RunReportingMeasures, + file: 'openstudio/workflow/jobs/run_reporting_measures.rb', options: {} }, + { state: :postprocess, next_state: :finished, job: :RunPostprocess, + file: 'openstudio/workflow/jobs/run_postprocess.rb', options: {} }, + { state: :finished }, + { state: :errored } + ] + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :errored + end +end + From 358afc7dddf47905a113b7c5b83212ac505ba5fc Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Fri, 27 Aug 2021 14:02:10 -0600 Subject: [PATCH 07/20] check version os before running epjson --- lib/openstudio/workflow/adapters/input/local.rb | 14 ++++++++++++-- lib/openstudio/workflow/run.rb | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/openstudio/workflow/adapters/input/local.rb b/lib/openstudio/workflow/adapters/input/local.rb index 0f2e50a..b3491fc 100644 --- a/lib/openstudio/workflow/adapters/input/local.rb +++ b/lib/openstudio/workflow/adapters/input/local.rb @@ -291,8 +291,18 @@ def verify_osw(user_options, default) return default end - def epjson(user_options, default) - # user option trumps all others + def epjson(user_options, default, logger) + + # check version for this feature + os_version = OpenStudio::VersionString.new(OpenStudio::openStudioVersion()) + min_version_epjson_feature = OpenStudio::VersionString.new("3.2.2") + unless os_version >= min_version_epjson_feature + log_message = "epJSON is only supporteded for versions >= 3.2.2. Falling back to using IDF" + logger.info log_message + return default + end + + # user option trumps all others return user_options[:epjson] if user_options[:epjson] return default diff --git a/lib/openstudio/workflow/run.rb b/lib/openstudio/workflow/run.rb index 2e8a6a7..0df76a0 100644 --- a/lib/openstudio/workflow/run.rb +++ b/lib/openstudio/workflow/run.rb @@ -200,7 +200,8 @@ def initialize(osw_path, user_options = {}) @options[:weather_file] = @input_adapter.weather_file(user_options, nil) @options[:fast] = @input_adapter.fast(user_options, false) @options[:skip_zip_results] = @input_adapter.skip_zip_results(user_options, false) - @options[:epjson] = @input_adapter.epjson(user_options, false) + @options[:epjson] = @input_adapter.epjson(user_options, false, @logger) + openstudio_dir = 'unknown' begin From 19106f658b722c1c5a772721c08c27650211c7fe Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Mon, 30 Aug 2021 14:42:23 -0600 Subject: [PATCH 08/20] don't save model_epjson in registry --- lib/openstudio/workflow/util/energyplus.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/openstudio/workflow/util/energyplus.rb b/lib/openstudio/workflow/util/energyplus.rb index 7f19401..79bd387 100644 --- a/lib/openstudio/workflow/util/energyplus.rb +++ b/lib/openstudio/workflow/util/energyplus.rb @@ -167,10 +167,9 @@ def call_energyplus(run_directory, energyplus_path = nil, output_adapter = nil, idf_final = load_idf('in.idf', @logger) model_epjson = translate_idf_to_epjson idf_final, @logger @registry[:time_logger]&.stop('Translating to EnergyPlus') - @registry.register(:model_epjson) { model_epjson } @logger.info 'Successfully translated to epJSON' @registry[:time_logger]&.start('Saving epJSON') - epjson_name = save_epjson(@registry[:model_epjson], run_directory) + epjson_name = save_epjson(model_epjson, run_directory) @registry[:time_logger]&.stop('Saving epJSON') @logger.debug "Saved epJSON as #{epjson_name}" end From b95238102794698e4af0cc0c3bcaf965d31a268f Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Fri, 3 Sep 2021 16:22:17 -0600 Subject: [PATCH 09/20] use full path to in.epJSON --- lib/openstudio/workflow/util/energyplus.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/openstudio/workflow/util/energyplus.rb b/lib/openstudio/workflow/util/energyplus.rb index 79bd387..06d6dc0 100644 --- a/lib/openstudio/workflow/util/energyplus.rb +++ b/lib/openstudio/workflow/util/energyplus.rb @@ -164,7 +164,7 @@ def call_energyplus(run_directory, energyplus_path = nil, output_adapter = nil, if @options[:epjson] @logger.info 'Beginning the translation to epJSON' @registry[:time_logger]&.start('Translating to EnergyPlus epJSON') - idf_final = load_idf('in.idf', @logger) + idf_final = load_idf("#{run_directory}/in.idf", @logger) model_epjson = translate_idf_to_epjson idf_final, @logger @registry[:time_logger]&.stop('Translating to EnergyPlus') @logger.info 'Successfully translated to epJSON' From c105657a47a60dcc1eb096fd24420fb39399662c Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 28 Sep 2021 12:00:11 +0200 Subject: [PATCH 10/20] Add ForwardTranslator CLI options --- .../workflow/adapters/input/local.rb | 25 +++++++++++++--- lib/openstudio/workflow/run.rb | 2 ++ lib/openstudio/workflow/util/energyplus.rb | 8 ++--- lib/openstudio/workflow/util/model.rb | 29 +++++++++++++++++-- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/lib/openstudio/workflow/adapters/input/local.rb b/lib/openstudio/workflow/adapters/input/local.rb index b3491fc..c7e3223 100644 --- a/lib/openstudio/workflow/adapters/input/local.rb +++ b/lib/openstudio/workflow/adapters/input/local.rb @@ -292,12 +292,12 @@ def verify_osw(user_options, default) end def epjson(user_options, default, logger) - + # check version for this feature os_version = OpenStudio::VersionString.new(OpenStudio::openStudioVersion()) - min_version_epjson_feature = OpenStudio::VersionString.new("3.2.2") - unless os_version >= min_version_epjson_feature - log_message = "epJSON is only supporteded for versions >= 3.2.2. Falling back to using IDF" + min_version_feature = OpenStudio::VersionString.new("3.3.0") + unless os_version >= min_version_feature + log_message = "epJSON is only supported for versions >= 3.3.0. Falling back to using IDF" logger.info log_message return default end @@ -308,6 +308,23 @@ def epjson(user_options, default, logger) return default end + def ft_options(user_options, default, logger) + + # check version for this feature + os_version = OpenStudio::VersionString.new(OpenStudio::openStudioVersion()) + min_version_feature = OpenStudio::VersionString.new("3.3.0") + unless os_version >= min_version_feature + log_message = "ft_options subhash are only supported for versions >= 3.3.0. Setting to empty by default" + logger.info log_message + return default + end + + # user option trumps all others + return user_options[:ft_options] if user_options[:ft_options] + + return default + end + def weather_file(user_options, default) # user option trumps all others return user_options[:weather_file] if user_options[:weather_file] diff --git a/lib/openstudio/workflow/run.rb b/lib/openstudio/workflow/run.rb index 0df76a0..f9ae3b0 100644 --- a/lib/openstudio/workflow/run.rb +++ b/lib/openstudio/workflow/run.rb @@ -96,6 +96,7 @@ def self.default_jobs # @option user_options [Hash] :verify_osw Check OSW for correctness, defaults to true # @option user_options [Hash] :weather_file Initial weather file to load, overrides OSW option if set, defaults to empty # @option user_options [Hash] :epjson - Create, export and run using epjson format. Default is IDF + # @option user_options [Hash] :ft_options - A subhash of options to set on the ForwardTranslator def initialize(osw_path, user_options = {}) # DLM - what is final_message? @final_message = '' @@ -201,6 +202,7 @@ def initialize(osw_path, user_options = {}) @options[:fast] = @input_adapter.fast(user_options, false) @options[:skip_zip_results] = @input_adapter.skip_zip_results(user_options, false) @options[:epjson] = @input_adapter.epjson(user_options, false, @logger) + @options[:ft_options] = @input_adapter.ft_options(user_options, {}, @logger) openstudio_dir = 'unknown' diff --git a/lib/openstudio/workflow/util/energyplus.rb b/lib/openstudio/workflow/util/energyplus.rb index 06d6dc0..689c8d5 100644 --- a/lib/openstudio/workflow/util/energyplus.rb +++ b/lib/openstudio/workflow/util/energyplus.rb @@ -157,10 +157,10 @@ def call_energyplus(run_directory, energyplus_path = nil, output_adapter = nil, end # Translate the IDF to an epJSON if @options[:epjson] is true - # Ideally, this would be done sooner in the workflow process but many processes - # manipulate the model_idf, some which are ep_measures that may not work with json + # Ideally, this would be done sooner in the workflow process but many processes + # manipulate the model_idf, some which are ep_measures that may not work with json # and ExpandObjects does not currently support epjson anyway to that still needs to run - # before this can be changed. + # before this can be changed. if @options[:epjson] @logger.info 'Beginning the translation to epJSON' @registry[:time_logger]&.start('Translating to EnergyPlus epJSON') @@ -174,7 +174,7 @@ def call_energyplus(run_directory, energyplus_path = nil, output_adapter = nil, @logger.debug "Saved epJSON as #{epjson_name}" end - # Run using epJSON if @options[:epjson] true, otherwise use ID + # Run using epJSON if @options[:epjson] true, otherwise use ID if @options[:epjson] command = popen_command("\"#{energyplus_exe}\" in.epJSON 2>&1") logger.info "Running command '#{command}'" diff --git a/lib/openstudio/workflow/util/model.rb b/lib/openstudio/workflow/util/model.rb index 802fc0d..8e9eac1 100644 --- a/lib/openstudio/workflow/util/model.rb +++ b/lib/openstudio/workflow/util/model.rb @@ -101,8 +101,33 @@ def translate_to_energyplus(model, logger = nil) # ensure objects exist for reporting purposes model.getFacility model.getBuilding - forward_translator = OpenStudio::EnergyPlus::ForwardTranslator.new - model_idf = forward_translator.translateModel(model) + ft = OpenStudio::EnergyPlus::ForwardTranslator.new + + ft_options = @options[:ft_options] + if !ft_options.empty? + mapping = { + :runcontrolspecialdays => :setKeepRunControlSpecialDays, + :ip_tabular_output => :setIPTabularOutput, + :no_lifecyclecosts => :setExcludeLCCObjects, + :no_sqlite_output => :setExcludeSQliteOutputReport, + :no_html_output => :setExcludeHTMLOutputReport, + :no_variable_dictionary => :setExcludeVariableDictionary, + :no_space_translation => :setExcludeSpaceTranslation, + } + + msg = "Custom ForwardTranslator options passed:\n" + mapping.each do |opt_flag_name, ft_method| + opt_flag = ft_options[opt_flag_name] + if !opt_flag.nil? + ft.method(ft_method).call(opt_flag) + msg += "* :#{opt_flag_name}=#{opt_flag} => ft.#{ft_method}(#{opt_flag})\n" + end + end + + logger.info msg + end + + model_idf = ft.translateModel(model) b = ::Time.now logger.info "Translate object to EnergyPlus IDF took #{b.to_f - a.to_f}" model_idf From f04f069deefc7f96a90471b525e3f5627524c657 Mon Sep 17 00:00:00 2001 From: Julien Marrec Date: Tue, 28 Sep 2021 12:59:01 +0200 Subject: [PATCH 11/20] Handle suboption validation in the InputAdapter and enhance the hash there with the method_name for the FT --- .../workflow/adapters/input/local.rb | 49 ++++++++++++++++--- lib/openstudio/workflow/util/model.rb | 25 ++++------ 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/lib/openstudio/workflow/adapters/input/local.rb b/lib/openstudio/workflow/adapters/input/local.rb index c7e3223..4847ee5 100644 --- a/lib/openstudio/workflow/adapters/input/local.rb +++ b/lib/openstudio/workflow/adapters/input/local.rb @@ -308,19 +308,52 @@ def epjson(user_options, default, logger) return default end + # Process the `run` method `ft_options` subhash + # + # This will validate that each suboption is supported in the current + # version as well as enhance the hash with the corresponding + # ForwardTranslator method name to set the value on the FT later + # in `translate_to_energyplus` def ft_options(user_options, default, logger) # check version for this feature os_version = OpenStudio::VersionString.new(OpenStudio::openStudioVersion()) - min_version_feature = OpenStudio::VersionString.new("3.3.0") - unless os_version >= min_version_feature - log_message = "ft_options subhash are only supported for versions >= 3.3.0. Setting to empty by default" - logger.info log_message - return default - end - # user option trumps all others - return user_options[:ft_options] if user_options[:ft_options] + os300 = OpenStudio::VersionString.new("3.0.0") + os330 = OpenStudio::VersionString.new("3.3.0") + known_ft_opts = { + # All Versions + :runcontrolspecialdays => {:method_name => :setKeepRunControlSpecialDays, :min_version => nil}, + :ip_tabular_output => {:method_name => :setIPTabularOutput, :min_version => nil}, + :no_lifecyclecosts => {:method_name => :setExcludeLCCObjects, :min_version => nil}, + # 3.0.0 + :no_sqlite_output => {:method_name => :setExcludeSQliteOutputReport, :min_version => os300}, + :no_html_output => {:method_name => :setExcludeHTMLOutputReport, :min_version => os300}, + :no_variable_dictionary => {:method_name => :setExcludeVariableDictionary, :min_version => os300}, + # 3.3.0 + :no_space_translation => {:method_name => :setExcludeSpaceTranslation, :min_version => os330}, + } + + if user_options[:ft_options] + ft_opts = {} + user_options[:ft_options].each do |opt_flag_name, opt_flag| + puts "#{opt_flag_name} = #{opt_flag}" + unless known_ft_opts.key?(opt_flag_name) + log_message = "'ft_options' suboption '#{opt_flag_name}' is not recognized, ignoring it." + logger.warn log_message + next + end + min_version = known_ft_opts[opt_flag_name][:min_version] + if !min_version.nil? && os_version < min_version + log_message = "'ft_options' suboption '#{opt_flag_name}' is only supported for OpenStudio Version >= #{min_version.str}, ignoring it." + logger.warn log_message + next + end + ft_opts[opt_flag_name] = {:method_name => known_ft_opts[opt_flag_name][:method_name], :value => opt_flag} + end + + return ft_opts + end return default end diff --git a/lib/openstudio/workflow/util/model.rb b/lib/openstudio/workflow/util/model.rb index 8e9eac1..c888f9e 100644 --- a/lib/openstudio/workflow/util/model.rb +++ b/lib/openstudio/workflow/util/model.rb @@ -105,23 +105,18 @@ def translate_to_energyplus(model, logger = nil) ft_options = @options[:ft_options] if !ft_options.empty? - mapping = { - :runcontrolspecialdays => :setKeepRunControlSpecialDays, - :ip_tabular_output => :setIPTabularOutput, - :no_lifecyclecosts => :setExcludeLCCObjects, - :no_sqlite_output => :setExcludeSQliteOutputReport, - :no_html_output => :setExcludeHTMLOutputReport, - :no_variable_dictionary => :setExcludeVariableDictionary, - :no_space_translation => :setExcludeSpaceTranslation, - } msg = "Custom ForwardTranslator options passed:\n" - mapping.each do |opt_flag_name, ft_method| - opt_flag = ft_options[opt_flag_name] - if !opt_flag.nil? - ft.method(ft_method).call(opt_flag) - msg += "* :#{opt_flag_name}=#{opt_flag} => ft.#{ft_method}(#{opt_flag})\n" - end + + ft_options.each do |opt_flag_name, h| + + ft_method = h[:method_name] + opt_flag = h[:value] + + # Call the FT setter with the value passed in + ft.method(ft_method).call(opt_flag) + + msg += "* :#{opt_flag_name}=#{opt_flag} => ft.#{ft_method}(#{opt_flag})\n" end logger.info msg From 4991a28cbee4b852b0a89a88ef80bf2e5c72b2dd Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Tue, 28 Sep 2021 21:02:01 -0600 Subject: [PATCH 12/20] adding some basic tests for translator options --- .../workflow_analysis_ft_options_spec.rb | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 spec/openstudio/workflow/workflow_analysis_ft_options_spec.rb diff --git a/spec/openstudio/workflow/workflow_analysis_ft_options_spec.rb b/spec/openstudio/workflow/workflow_analysis_ft_options_spec.rb new file mode 100644 index 0000000..704ab94 --- /dev/null +++ b/spec/openstudio/workflow/workflow_analysis_ft_options_spec.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +# ******************************************************************************* +# OpenStudio(R), Copyright (c) 2008-2021, 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 HOLDERS AND 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, THE UNITED STATES +# GOVERNMENT, OR ANY CONTRIBUTORS 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. +# ******************************************************************************* + +require_relative './../../spec_helper' +require 'json-schema' + +describe 'OSW Integration' do + it 'should run empty OSW file' do + osw_path = File.join(__FILE__, './../../../files/empty_seed_osw/empty.osw') + run_options = { + debug: true, + epjson: true + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + end + + it 'should run compact OSW file with translator options (no space translation)' do + osw_path = File.expand_path('../../files/compact_osw/compact.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + debug: true, + runcontrolspecialdays: true, + ip_tabular_output: true, + no_lifecyclecosts: true, + no_sqlite_output: true, + no_html_output: true, + no_variable_dictionary: true, + no_space_translation: true + + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to_not be_nil + end + end + + it 'should run compact OSW file with translator options (space translation)' do + osw_path = File.expand_path('../../files/compact_osw/compact.osw', __dir__) + osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') + + FileUtils.rm_rf(osw_out_path) if File.exist?(osw_out_path) + expect(File.exist?(osw_out_path)).to eq false + + run_options = { + + debug: true, + ft_options: { + runcontrolspecialdays: false, + ip_tabular_output: false, + no_lifecyclecosts: false, + no_sqlite_output: false, + no_html_output: false, + no_variable_dictionary: false, + no_space_translation: false + } + + } + k = OpenStudio::Workflow::Run.new osw_path, run_options + expect(k).to be_instance_of OpenStudio::Workflow::Run + expect(k.run).to eq :finished + + expect(File.exist?(osw_out_path)).to eq true + + osw_out = nil + File.open(osw_out_path, 'r') do |file| + osw_out = JSON.parse(file.read, symbolize_names: true) + end + + expect(osw_out).to be_instance_of Hash + expect(osw_out[:completed_status]).to eq 'Success' + expect(osw_out[:steps]).to be_instance_of Array + expect(osw_out[:steps].size).to be > 0 + osw_out[:steps].each do |step| + expect(step[:result]).to_not be_nil + end + end + +end + From a65f4b9999c9f1b5062583816779c816263a8aea Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Tue, 28 Sep 2021 21:27:27 -0600 Subject: [PATCH 13/20] add ft_options to hash --- .../workflow_analysis_ft_options_spec.rb | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/spec/openstudio/workflow/workflow_analysis_ft_options_spec.rb b/spec/openstudio/workflow/workflow_analysis_ft_options_spec.rb index 704ab94..7755bee 100644 --- a/spec/openstudio/workflow/workflow_analysis_ft_options_spec.rb +++ b/spec/openstudio/workflow/workflow_analysis_ft_options_spec.rb @@ -50,7 +50,7 @@ expect(k.run).to eq :finished end - it 'should run compact OSW file with translator options (no space translation)' do + it 'should run compact OSW file with translator options 1' do osw_path = File.expand_path('../../files/compact_osw/compact.osw', __dir__) osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') @@ -59,14 +59,15 @@ run_options = { debug: true, - runcontrolspecialdays: true, - ip_tabular_output: true, - no_lifecyclecosts: true, - no_sqlite_output: true, - no_html_output: true, - no_variable_dictionary: true, - no_space_translation: true - + ft_options: { + runcontrolspecialdays: true, + ip_tabular_output: true, + no_lifecyclecosts: true, + no_sqlite_output: true, + no_html_output: true, + no_variable_dictionary: true, + no_space_translation: true + } } k = OpenStudio::Workflow::Run.new osw_path, run_options expect(k).to be_instance_of OpenStudio::Workflow::Run @@ -88,7 +89,7 @@ end end - it 'should run compact OSW file with translator options (space translation)' do + it 'should run compact OSW file with translator options 2' do osw_path = File.expand_path('../../files/compact_osw/compact.osw', __dir__) osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') From 73209dea95b96c3f98fe0d4518ab07ca19f74255 Mon Sep 17 00:00:00 2001 From: TJC Date: Wed, 6 Oct 2021 09:42:18 -0600 Subject: [PATCH 14/20] Update lib/openstudio/workflow/util/energyplus.rb Co-authored-by: Dan Macumber --- lib/openstudio/workflow/util/energyplus.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/openstudio/workflow/util/energyplus.rb b/lib/openstudio/workflow/util/energyplus.rb index 689c8d5..206db62 100644 --- a/lib/openstudio/workflow/util/energyplus.rb +++ b/lib/openstudio/workflow/util/energyplus.rb @@ -174,7 +174,7 @@ def call_energyplus(run_directory, energyplus_path = nil, output_adapter = nil, @logger.debug "Saved epJSON as #{epjson_name}" end - # Run using epJSON if @options[:epjson] true, otherwise use ID + # Run using epJSON if @options[:epjson] true, otherwise use IDF if @options[:epjson] command = popen_command("\"#{energyplus_exe}\" in.epJSON 2>&1") logger.info "Running command '#{command}'" From 9fb78258dddfdc126aa33f6fd341db33c4679eea Mon Sep 17 00:00:00 2001 From: tijcolem Date: Wed, 13 Oct 2021 11:58:42 -0600 Subject: [PATCH 15/20] try and read from osw for ft_options and epjson --- .../workflow/adapters/input/local.rb | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/openstudio/workflow/adapters/input/local.rb b/lib/openstudio/workflow/adapters/input/local.rb index 4847ee5..65a674d 100644 --- a/lib/openstudio/workflow/adapters/input/local.rb +++ b/lib/openstudio/workflow/adapters/input/local.rb @@ -305,6 +305,11 @@ def epjson(user_options, default, logger) # user option trumps all others return user_options[:epjson] if user_options[:epjson] + # try to read from OSW + if @run_options && !@run_options.get.epjson.empty? + return @run_options.get.epjson + end + return default end @@ -333,6 +338,7 @@ def ft_options(user_options, default, logger) # 3.3.0 :no_space_translation => {:method_name => :setExcludeSpaceTranslation, :min_version => os330}, } + if user_options[:ft_options] ft_opts = {} @@ -355,6 +361,27 @@ def ft_options(user_options, default, logger) return ft_opts end + if @run_options && !@run_options.get.ft_options.empty? + ft_opts = {} + @run_options.get.ft_options.each do |opt_flag_name, opt_flag| + puts "#{opt_flag_name} = #{opt_flag}" + unless known_ft_opts.key?(opt_flag_name) + log_message = "'ft_options' suboption '#{opt_flag_name}' is not recognized, ignoring it." + logger.warn log_message + next + end + min_version = known_ft_opts[opt_flag_name][:min_version] + if !min_version.nil? && os_version < min_version + log_message = "'ft_options' suboption '#{opt_flag_name}' is only supported for OpenStudio Version >= #{min_version.str}, ignoring it." + logger.warn log_message + next + end + ft_opts[opt_flag_name] = {:method_name => known_ft_opts[opt_flag_name][:method_name], :value => opt_flag} + end + + return ft_opts + end + return default end From 803fac91b555a8a3d17ddca980a1621be3c9502a Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Wed, 13 Oct 2021 20:36:05 -0600 Subject: [PATCH 16/20] update ft_options to read from osw --- lib/openstudio/workflow/adapters/input/local.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/openstudio/workflow/adapters/input/local.rb b/lib/openstudio/workflow/adapters/input/local.rb index 65a674d..3a0551b 100644 --- a/lib/openstudio/workflow/adapters/input/local.rb +++ b/lib/openstudio/workflow/adapters/input/local.rb @@ -302,12 +302,12 @@ def epjson(user_options, default, logger) return default end - # user option trumps all others + # user option trumps all others return user_options[:epjson] if user_options[:epjson] # try to read from OSW - if @run_options && !@run_options.get.epjson.empty? - return @run_options.get.epjson + if @run_options && @run_options.get.respond_to?(:epjson) + return @run_options.get.epjson end return default @@ -319,6 +319,7 @@ def epjson(user_options, default, logger) # version as well as enhance the hash with the corresponding # ForwardTranslator method name to set the value on the FT later # in `translate_to_energyplus` + # user option trumps all others def ft_options(user_options, default, logger) # check version for this feature @@ -339,7 +340,7 @@ def ft_options(user_options, default, logger) :no_space_translation => {:method_name => :setExcludeSpaceTranslation, :min_version => os330}, } - + #user option trumps all others if user_options[:ft_options] ft_opts = {} user_options[:ft_options].each do |opt_flag_name, opt_flag| @@ -361,16 +362,16 @@ def ft_options(user_options, default, logger) return ft_opts end - if @run_options && !@run_options.get.ft_options.empty? + # try to read from OSW + if @run_options &&@run_options.get.respond_to?(:forwardTranslateOptions) and !@run_options.get.forwardTranslateOptions.empty? ft_opts = {} - @run_options.get.ft_options.each do |opt_flag_name, opt_flag| - puts "#{opt_flag_name} = #{opt_flag}" + JSON.parse(@run_options.get.forwardTranslateOptions, :symbolize_names => true).each do |opt_flag_name, opt_flag| unless known_ft_opts.key?(opt_flag_name) log_message = "'ft_options' suboption '#{opt_flag_name}' is not recognized, ignoring it." logger.warn log_message next end - min_version = known_ft_opts[opt_flag_name][:min_version] + min_version = known_ft_opts[opt_flag_name.to_sym][:min_version] if !min_version.nil? && os_version < min_version log_message = "'ft_options' suboption '#{opt_flag_name}' is only supported for OpenStudio Version >= #{min_version.str}, ignoring it." logger.warn log_message From 69d8ffb43d3951e4ff35534658ec893949a6c9d4 Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Thu, 14 Oct 2021 10:06:25 -0600 Subject: [PATCH 17/20] add check to see if run_options is initialized --- lib/openstudio/workflow/adapters/input/local.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/openstudio/workflow/adapters/input/local.rb b/lib/openstudio/workflow/adapters/input/local.rb index 3a0551b..eddd61b 100644 --- a/lib/openstudio/workflow/adapters/input/local.rb +++ b/lib/openstudio/workflow/adapters/input/local.rb @@ -306,8 +306,9 @@ def epjson(user_options, default, logger) return user_options[:epjson] if user_options[:epjson] # try to read from OSW - if @run_options && @run_options.get.respond_to?(:epjson) - return @run_options.get.epjson + + if @run_options.is_initialized && @run_options.get.respond_to?(:epjson) + return @run_options.get.epjson end return default @@ -363,10 +364,11 @@ def ft_options(user_options, default, logger) end # try to read from OSW - if @run_options &&@run_options.get.respond_to?(:forwardTranslateOptions) and !@run_options.get.forwardTranslateOptions.empty? + + if @run_options.is_initialized && @run_options.get.respond_to?(:forwardTranslateOptions) ft_opts = {} - JSON.parse(@run_options.get.forwardTranslateOptions, :symbolize_names => true).each do |opt_flag_name, opt_flag| - unless known_ft_opts.key?(opt_flag_name) + JSON.parse(@run_options.get.forwardTranslateOptions, :symbolize_names => true).each do |opt_flag_name, opt_flag| + unless known_ft_opts.key?(opt_flag_name) log_message = "'ft_options' suboption '#{opt_flag_name}' is not recognized, ignoring it." logger.warn log_message next From 7ffcf0e583012c034e1cdd2824bf23b75a9c7c40 Mon Sep 17 00:00:00 2001 From: Tim Coleman Date: Wed, 20 Oct 2021 08:32:07 -0600 Subject: [PATCH 18/20] update json schema for forward translate and epjson runtime options --- spec/schema/osw.json | 26 ++++++++++++++++++++++++++ spec/schema/osw_output.json | 26 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/spec/schema/osw.json b/spec/schema/osw.json index 67783bd..4f251cd 100755 --- a/spec/schema/osw.json +++ b/spec/schema/osw.json @@ -98,6 +98,32 @@ "cleanup": { "type": "boolean" }, + "epjson": { + "type": "boolean" + }, + "ft_options": { + "runcontrolspecialdays": { + "type": "boolean" + }, + "ip_tabular_output": { + "type": "boolean" + }, + "no_lifecyclecosts": { + "type": "boolean" + }, + "no_sqlite_output": { + "type": "boolean" + }, + "no_html_output": { + "type": "boolean" + }, + "no_variable_dictionary": { + "type": "boolean" + }, + "no_space_translation": { + "type": "boolean" + } + }, "output_adapter": { "type": "object", "oneOf": [ diff --git a/spec/schema/osw_output.json b/spec/schema/osw_output.json index 535b325..703fe7d 100644 --- a/spec/schema/osw_output.json +++ b/spec/schema/osw_output.json @@ -125,6 +125,32 @@ "cleanup": { "type": "boolean" }, + "epjson": { + "type": "boolean" + }, + "ft_options": { + "runcontrolspecialdays": { + "type": "boolean" + }, + "ip_tabular_output": { + "type": "boolean" + }, + "no_lifecyclecosts": { + "type": "boolean" + }, + "no_sqlite_output": { + "type": "boolean" + }, + "no_html_output": { + "type": "boolean" + }, + "no_variable_dictionary": { + "type": "boolean" + }, + "no_space_translation": { + "type": "boolean" + } + }, "output_adapter": { "type": "object", "oneOf": [ From ea2ce705dca5c2fca9f113e9e21d098a2a21df02 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Thu, 21 Oct 2021 06:32:52 -0600 Subject: [PATCH 19/20] bump version and code cleanup --- .../workflow/adapters/input/local.rb | 40 +++++++++---------- lib/openstudio/workflow/run.rb | 3 +- lib/openstudio/workflow/util/model.rb | 6 +-- lib/openstudio/workflow/version.rb | 2 +- .../workflow/workflow_analysis_epjson_spec.rb | 3 +- .../workflow_analysis_ft_options_spec.rb | 2 - 6 files changed, 25 insertions(+), 31 deletions(-) diff --git a/lib/openstudio/workflow/adapters/input/local.rb b/lib/openstudio/workflow/adapters/input/local.rb index eddd61b..03ea4f6 100644 --- a/lib/openstudio/workflow/adapters/input/local.rb +++ b/lib/openstudio/workflow/adapters/input/local.rb @@ -292,12 +292,11 @@ def verify_osw(user_options, default) end def epjson(user_options, default, logger) - # check version for this feature - os_version = OpenStudio::VersionString.new(OpenStudio::openStudioVersion()) - min_version_feature = OpenStudio::VersionString.new("3.3.0") + os_version = OpenStudio::VersionString.new(OpenStudio.openStudioVersion) + min_version_feature = OpenStudio::VersionString.new('3.3.0') unless os_version >= min_version_feature - log_message = "epJSON is only supported for versions >= 3.3.0. Falling back to using IDF" + log_message = 'epJSON is only supported for versions >= 3.3.0. Falling back to using IDF' logger.info log_message return default end @@ -322,26 +321,25 @@ def epjson(user_options, default, logger) # in `translate_to_energyplus` # user option trumps all others def ft_options(user_options, default, logger) - # check version for this feature - os_version = OpenStudio::VersionString.new(OpenStudio::openStudioVersion()) + os_version = OpenStudio::VersionString.new(OpenStudio.openStudioVersion) - os300 = OpenStudio::VersionString.new("3.0.0") - os330 = OpenStudio::VersionString.new("3.3.0") + os300 = OpenStudio::VersionString.new('3.0.0') + os330 = OpenStudio::VersionString.new('3.3.0') known_ft_opts = { # All Versions - :runcontrolspecialdays => {:method_name => :setKeepRunControlSpecialDays, :min_version => nil}, - :ip_tabular_output => {:method_name => :setIPTabularOutput, :min_version => nil}, - :no_lifecyclecosts => {:method_name => :setExcludeLCCObjects, :min_version => nil}, + runcontrolspecialdays: { method_name: :setKeepRunControlSpecialDays, min_version: nil }, + ip_tabular_output: { method_name: :setIPTabularOutput, min_version: nil }, + no_lifecyclecosts: { method_name: :setExcludeLCCObjects, min_version: nil }, # 3.0.0 - :no_sqlite_output => {:method_name => :setExcludeSQliteOutputReport, :min_version => os300}, - :no_html_output => {:method_name => :setExcludeHTMLOutputReport, :min_version => os300}, - :no_variable_dictionary => {:method_name => :setExcludeVariableDictionary, :min_version => os300}, + no_sqlite_output: { method_name: :setExcludeSQliteOutputReport, min_version: os300 }, + no_html_output: { method_name: :setExcludeHTMLOutputReport, min_version: os300 }, + no_variable_dictionary: { method_name: :setExcludeVariableDictionary, min_version: os300 }, # 3.3.0 - :no_space_translation => {:method_name => :setExcludeSpaceTranslation, :min_version => os330}, + no_space_translation: { method_name: :setExcludeSpaceTranslation, min_version: os330 } } - - #user option trumps all others + + # user option trumps all others if user_options[:ft_options] ft_opts = {} user_options[:ft_options].each do |opt_flag_name, opt_flag| @@ -357,7 +355,7 @@ def ft_options(user_options, default, logger) logger.warn log_message next end - ft_opts[opt_flag_name] = {:method_name => known_ft_opts[opt_flag_name][:method_name], :value => opt_flag} + ft_opts[opt_flag_name] = { method_name: known_ft_opts[opt_flag_name][:method_name], value: opt_flag } end return ft_opts @@ -367,8 +365,8 @@ def ft_options(user_options, default, logger) if @run_options.is_initialized && @run_options.get.respond_to?(:forwardTranslateOptions) ft_opts = {} - JSON.parse(@run_options.get.forwardTranslateOptions, :symbolize_names => true).each do |opt_flag_name, opt_flag| - unless known_ft_opts.key?(opt_flag_name) + JSON.parse(@run_options.get.forwardTranslateOptions, symbolize_names: true).each do |opt_flag_name, opt_flag| + unless known_ft_opts.key?(opt_flag_name) log_message = "'ft_options' suboption '#{opt_flag_name}' is not recognized, ignoring it." logger.warn log_message next @@ -379,7 +377,7 @@ def ft_options(user_options, default, logger) logger.warn log_message next end - ft_opts[opt_flag_name] = {:method_name => known_ft_opts[opt_flag_name][:method_name], :value => opt_flag} + ft_opts[opt_flag_name] = { method_name: known_ft_opts[opt_flag_name][:method_name], value: opt_flag } end return ft_opts diff --git a/lib/openstudio/workflow/run.rb b/lib/openstudio/workflow/run.rb index f9ae3b0..5763157 100644 --- a/lib/openstudio/workflow/run.rb +++ b/lib/openstudio/workflow/run.rb @@ -202,8 +202,7 @@ def initialize(osw_path, user_options = {}) @options[:fast] = @input_adapter.fast(user_options, false) @options[:skip_zip_results] = @input_adapter.skip_zip_results(user_options, false) @options[:epjson] = @input_adapter.epjson(user_options, false, @logger) - @options[:ft_options] = @input_adapter.ft_options(user_options, {}, @logger) - + @options[:ft_options] = @input_adapter.ft_options(user_options, {}, @logger) openstudio_dir = 'unknown' begin diff --git a/lib/openstudio/workflow/util/model.rb b/lib/openstudio/workflow/util/model.rb index c888f9e..86ae0bc 100644 --- a/lib/openstudio/workflow/util/model.rb +++ b/lib/openstudio/workflow/util/model.rb @@ -109,7 +109,6 @@ def translate_to_energyplus(model, logger = nil) msg = "Custom ForwardTranslator options passed:\n" ft_options.each do |opt_flag_name, h| - ft_method = h[:method_name] opt_flag = h[:value] @@ -127,7 +126,8 @@ def translate_to_energyplus(model, logger = nil) logger.info "Translate object to EnergyPlus IDF took #{b.to_f - a.to_f}" model_idf end - # Translates an IDF model into an EnergyPlus epJSON object + + # Translates an IDF model into an EnergyPlus epJSON object # # @param [Object] OpenStudio::IdfFile instance to translate into an OpenStudio epJSON object -- see # the OpenStudio SDK for details on the process @@ -137,7 +137,7 @@ def translate_idf_to_epjson(model_idf, logger = nil) logger ||= ::Logger.new($stdout) logger.info 'Translate IDF to epJSON in preparation for EnergyPlus' a = ::Time.now - model_epjson = OpenStudio::EPJSON::toJSONString(model_idf) + model_epjson = OpenStudio::EPJSON.toJSONString(model_idf) b = ::Time.now logger.info "Translate IDF to EnergyPlus epJSON took #{b.to_f - a.to_f}" diff --git a/lib/openstudio/workflow/version.rb b/lib/openstudio/workflow/version.rb index 8972164..4823217 100644 --- a/lib/openstudio/workflow/version.rb +++ b/lib/openstudio/workflow/version.rb @@ -37,6 +37,6 @@ module OpenStudio module Workflow - VERSION = '2.2.2' # Suffixes must have periods (not dashes) + VERSION = '2.3.0' # Suffixes must have periods (not dashes) end end diff --git a/spec/openstudio/workflow/workflow_analysis_epjson_spec.rb b/spec/openstudio/workflow/workflow_analysis_epjson_spec.rb index 77bd544..971710e 100644 --- a/spec/openstudio/workflow/workflow_analysis_epjson_spec.rb +++ b/spec/openstudio/workflow/workflow_analysis_epjson_spec.rb @@ -80,7 +80,7 @@ expect(step[:result]).to_not be_nil end end - + it 'should run compact OSW file in m and w and p mode' do osw_path = File.expand_path('../../files/compact_mwp_osw/compact_mwp.osw', __dir__) osw_out_path = osw_path.gsub(File.basename(osw_path), 'out.osw') @@ -1259,4 +1259,3 @@ expect(k.run).to eq :errored end end - diff --git a/spec/openstudio/workflow/workflow_analysis_ft_options_spec.rb b/spec/openstudio/workflow/workflow_analysis_ft_options_spec.rb index 7755bee..8e13bb6 100644 --- a/spec/openstudio/workflow/workflow_analysis_ft_options_spec.rb +++ b/spec/openstudio/workflow/workflow_analysis_ft_options_spec.rb @@ -129,6 +129,4 @@ expect(step[:result]).to_not be_nil end end - end - From 3b90841e5f263f12b27a36d8d1cacac3f64e97c5 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Thu, 21 Oct 2021 06:35:02 -0600 Subject: [PATCH 20/20] add changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1e3555..3995053 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ OpenStudio::Workflow Change Log ================================== +Version 2.3.0 +------------- +* Feature [#135](https://github.com/NREL/OpenStudio-workflow-gem/issues/135) Add option to export/run epJSON + Version 2.2.1 ------------- * Fixes [#4150](https://github.com/NREL/OpenStudio/issues/4150) LoadError changes current working directory in CLI