openstudio-workflow 2.2.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 03716fad02e514aa60fbb2e3a14dd2c50286fe2621219f63137f4e1bb77afcb9
4
- data.tar.gz: 525a28b94195445ccf890f784898f9868226aea11d0137d112223c2ea0abd099
3
+ metadata.gz: fcb791c63593c4b0a7c7050c243c4e7214e8e62f04d3f4e3c29652c6690b5372
4
+ data.tar.gz: c831e36b5024f861eee6a46e5fc751ec0dc965a4e358fd0532ad293069acfc4f
5
5
  SHA512:
6
- metadata.gz: bc22ddb2820ea5155ce5308a49cfc405f5bdfa43a6b54fbac46ad8486deadb0f8bd866305566dc6fc70d65dbe71feb2c249fd6c0fa94623427f27b630b4c6fc7
7
- data.tar.gz: de613152849f97dd568c2f91a3411cb0c3a0f37d48f3e86a3fd37e7922d074e058eab6515e82f4359edb3e175baa718a91c8cbf6cbe1cc414afacee0d9c3253a
6
+ metadata.gz: 5191c3ec1be4a4b801afb357f46ef35b127a73b92c0c0d5c2831f1bb3374ec365675c6baa70e2513884ab87d5f6ffb80f69e540f90310db6ea99cf1d1962fe56
7
+ data.tar.gz: 7aceb6c2b096e47522ed68b978f3e36d46c8dd2dd79ace0a7b5415a685078dfd9852f75e626c98ea2d875e4365ae5787ea6a5007178fbda5b84490868b92ff30
data/CHANGELOG.md CHANGED
@@ -1,6 +1,10 @@
1
1
  OpenStudio::Workflow Change Log
2
2
  ==================================
3
3
 
4
+ Version 2.3.0
5
+ -------------
6
+ * Feature [#135](https://github.com/NREL/OpenStudio-workflow-gem/issues/135) Add option to export/run epJSON
7
+
4
8
  Version 2.2.1
5
9
  -------------
6
10
  * Fixes [#4150](https://github.com/NREL/OpenStudio/issues/4150) LoadError changes current working directory in CLI
@@ -291,6 +291,101 @@ module OpenStudio
291
291
  return default
292
292
  end
293
293
 
294
+ def epjson(user_options, default, logger)
295
+ # check version for this feature
296
+ os_version = OpenStudio::VersionString.new(OpenStudio.openStudioVersion)
297
+ min_version_feature = OpenStudio::VersionString.new('3.3.0')
298
+ unless os_version >= min_version_feature
299
+ log_message = 'epJSON is only supported for versions >= 3.3.0. Falling back to using IDF'
300
+ logger.info log_message
301
+ return default
302
+ end
303
+
304
+ # user option trumps all others
305
+ return user_options[:epjson] if user_options[:epjson]
306
+
307
+ # try to read from OSW
308
+
309
+ if @run_options.is_initialized && @run_options.get.respond_to?(:epjson)
310
+ return @run_options.get.epjson
311
+ end
312
+
313
+ return default
314
+ end
315
+
316
+ # Process the `run` method `ft_options` subhash
317
+ #
318
+ # This will validate that each suboption is supported in the current
319
+ # version as well as enhance the hash with the corresponding
320
+ # ForwardTranslator method name to set the value on the FT later
321
+ # in `translate_to_energyplus`
322
+ # user option trumps all others
323
+ def ft_options(user_options, default, logger)
324
+ # check version for this feature
325
+ os_version = OpenStudio::VersionString.new(OpenStudio.openStudioVersion)
326
+
327
+ os300 = OpenStudio::VersionString.new('3.0.0')
328
+ os330 = OpenStudio::VersionString.new('3.3.0')
329
+ known_ft_opts = {
330
+ # All Versions
331
+ runcontrolspecialdays: { method_name: :setKeepRunControlSpecialDays, min_version: nil },
332
+ ip_tabular_output: { method_name: :setIPTabularOutput, min_version: nil },
333
+ no_lifecyclecosts: { method_name: :setExcludeLCCObjects, min_version: nil },
334
+ # 3.0.0
335
+ no_sqlite_output: { method_name: :setExcludeSQliteOutputReport, min_version: os300 },
336
+ no_html_output: { method_name: :setExcludeHTMLOutputReport, min_version: os300 },
337
+ no_variable_dictionary: { method_name: :setExcludeVariableDictionary, min_version: os300 },
338
+ # 3.3.0
339
+ no_space_translation: { method_name: :setExcludeSpaceTranslation, min_version: os330 }
340
+ }
341
+
342
+ # user option trumps all others
343
+ if user_options[:ft_options]
344
+ ft_opts = {}
345
+ user_options[:ft_options].each do |opt_flag_name, opt_flag|
346
+ puts "#{opt_flag_name} = #{opt_flag}"
347
+ unless known_ft_opts.key?(opt_flag_name)
348
+ log_message = "'ft_options' suboption '#{opt_flag_name}' is not recognized, ignoring it."
349
+ logger.warn log_message
350
+ next
351
+ end
352
+ min_version = known_ft_opts[opt_flag_name][:min_version]
353
+ if !min_version.nil? && os_version < min_version
354
+ log_message = "'ft_options' suboption '#{opt_flag_name}' is only supported for OpenStudio Version >= #{min_version.str}, ignoring it."
355
+ logger.warn log_message
356
+ next
357
+ end
358
+ ft_opts[opt_flag_name] = { method_name: known_ft_opts[opt_flag_name][:method_name], value: opt_flag }
359
+ end
360
+
361
+ return ft_opts
362
+ end
363
+
364
+ # try to read from OSW
365
+
366
+ if @run_options.is_initialized && @run_options.get.respond_to?(:forwardTranslateOptions)
367
+ ft_opts = {}
368
+ JSON.parse(@run_options.get.forwardTranslateOptions, symbolize_names: true).each do |opt_flag_name, opt_flag|
369
+ unless known_ft_opts.key?(opt_flag_name)
370
+ log_message = "'ft_options' suboption '#{opt_flag_name}' is not recognized, ignoring it."
371
+ logger.warn log_message
372
+ next
373
+ end
374
+ min_version = known_ft_opts[opt_flag_name.to_sym][:min_version]
375
+ if !min_version.nil? && os_version < min_version
376
+ log_message = "'ft_options' suboption '#{opt_flag_name}' is only supported for OpenStudio Version >= #{min_version.str}, ignoring it."
377
+ logger.warn log_message
378
+ next
379
+ end
380
+ ft_opts[opt_flag_name] = { method_name: known_ft_opts[opt_flag_name][:method_name], value: opt_flag }
381
+ end
382
+
383
+ return ft_opts
384
+ end
385
+
386
+ return default
387
+ end
388
+
294
389
  def weather_file(user_options, default)
295
390
  # user option trumps all others
296
391
  return user_options[:weather_file] if user_options[:weather_file]
@@ -95,6 +95,8 @@ module OpenStudio
95
95
  # @option user_options [Hash] :targets Log targets to write to, defaults to standard out and run.log
96
96
  # @option user_options [Hash] :verify_osw Check OSW for correctness, defaults to true
97
97
  # @option user_options [Hash] :weather_file Initial weather file to load, overrides OSW option if set, defaults to empty
98
+ # @option user_options [Hash] :epjson - Create, export and run using epjson format. Default is IDF
99
+ # @option user_options [Hash] :ft_options - A subhash of options to set on the ForwardTranslator
98
100
  def initialize(osw_path, user_options = {})
99
101
  # DLM - what is final_message?
100
102
  @final_message = ''
@@ -199,6 +201,8 @@ module OpenStudio
199
201
  @options[:weather_file] = @input_adapter.weather_file(user_options, nil)
200
202
  @options[:fast] = @input_adapter.fast(user_options, false)
201
203
  @options[:skip_zip_results] = @input_adapter.skip_zip_results(user_options, false)
204
+ @options[:epjson] = @input_adapter.epjson(user_options, false, @logger)
205
+ @options[:ft_options] = @input_adapter.ft_options(user_options, {}, @logger)
202
206
 
203
207
  openstudio_dir = 'unknown'
204
208
  begin
@@ -43,6 +43,8 @@ module OpenStudio
43
43
  module EnergyPlus
44
44
  require 'openstudio/workflow/util/io'
45
45
  include OpenStudio::Workflow::Util::IO
46
+ require 'openstudio/workflow/util/model'
47
+ include OpenStudio::Workflow::Util::Model
46
48
  ENERGYPLUS_REGEX = /^energyplus\D{0,4}$/i.freeze
47
49
  EXPAND_OBJECTS_REGEX = /^expandobjects\D{0,4}$/i.freeze
48
50
 
@@ -94,8 +96,8 @@ module OpenStudio
94
96
  Dir["#{energyplus_path}/*"].each do |file|
95
97
  next if File.directory? file
96
98
 
97
- # copy idd and ini files
98
- if File.extname(file).downcase =~ /.idd|.ini/
99
+ # copy idd, ini and epJSON schema files
100
+ if File.extname(file).downcase =~ /.idd|.ini|.epjson/
99
101
  dest_file = "#{run_directory}/#{File.basename(file)}"
100
102
  energyplus_files << dest_file
101
103
  FileUtils.copy file, dest_file
@@ -130,7 +132,9 @@ module OpenStudio
130
132
  current_dir = Dir.pwd
131
133
  energyplus_path ||= find_energyplus
132
134
  logger.info "EnergyPlus path is #{energyplus_path}"
135
+
133
136
  energyplus_files, energyplus_exe, expand_objects_exe = prepare_energyplus_dir(run_directory, logger, energyplus_path)
137
+
134
138
  Dir.chdir(run_directory)
135
139
  logger.info "Starting simulation in run directory: #{Dir.pwd}"
136
140
 
@@ -152,18 +156,50 @@ module OpenStudio
152
156
  end
153
157
  end
154
158
 
155
- # create stdout
156
- command = popen_command("\"#{energyplus_exe}\" 2>&1")
157
- logger.info "Running command '#{command}'"
158
- File.open('stdout-energyplus', 'w') do |file|
159
- ::IO.popen(command) do |io|
160
- while (line = io.gets)
161
- file << line
162
- output_adapter&.communicate_energyplus_stdout(line)
159
+ # Translate the IDF to an epJSON if @options[:epjson] is true
160
+ # Ideally, this would be done sooner in the workflow process but many processes
161
+ # manipulate the model_idf, some which are ep_measures that may not work with json
162
+ # and ExpandObjects does not currently support epjson anyway to that still needs to run
163
+ # before this can be changed.
164
+ if @options[:epjson]
165
+ @logger.info 'Beginning the translation to epJSON'
166
+ @registry[:time_logger]&.start('Translating to EnergyPlus epJSON')
167
+ idf_final = load_idf("#{run_directory}/in.idf", @logger)
168
+ model_epjson = translate_idf_to_epjson idf_final, @logger
169
+ @registry[:time_logger]&.stop('Translating to EnergyPlus')
170
+ @logger.info 'Successfully translated to epJSON'
171
+ @registry[:time_logger]&.start('Saving epJSON')
172
+ epjson_name = save_epjson(model_epjson, run_directory)
173
+ @registry[:time_logger]&.stop('Saving epJSON')
174
+ @logger.debug "Saved epJSON as #{epjson_name}"
175
+ end
176
+
177
+ # Run using epJSON if @options[:epjson] true, otherwise use IDF
178
+ if @options[:epjson]
179
+ command = popen_command("\"#{energyplus_exe}\" in.epJSON 2>&1")
180
+ logger.info "Running command '#{command}'"
181
+ File.open('stdout-energyplus', 'w') do |file|
182
+ ::IO.popen(command) do |io|
183
+ while (line = io.gets)
184
+ file << line
185
+ output_adapter&.communicate_energyplus_stdout(line)
186
+ end
187
+ end
188
+ end
189
+ r = $?
190
+ else
191
+ command = popen_command("\"#{energyplus_exe}\" 2>&1")
192
+ logger.info "Running command '#{command}'"
193
+ File.open('stdout-energyplus', 'w') do |file|
194
+ ::IO.popen(command) do |io|
195
+ while (line = io.gets)
196
+ file << line
197
+ output_adapter&.communicate_energyplus_stdout(line)
198
+ end
163
199
  end
164
200
  end
201
+ r = $?
165
202
  end
166
- r = $?
167
203
 
168
204
  logger.info "EnergyPlus returned '#{r}'"
169
205
  unless r.to_i.zero?
@@ -101,13 +101,49 @@ module OpenStudio
101
101
  # ensure objects exist for reporting purposes
102
102
  model.getFacility
103
103
  model.getBuilding
104
- forward_translator = OpenStudio::EnergyPlus::ForwardTranslator.new
105
- model_idf = forward_translator.translateModel(model)
104
+ ft = OpenStudio::EnergyPlus::ForwardTranslator.new
105
+
106
+ ft_options = @options[:ft_options]
107
+ if !ft_options.empty?
108
+
109
+ msg = "Custom ForwardTranslator options passed:\n"
110
+
111
+ ft_options.each do |opt_flag_name, h|
112
+ ft_method = h[:method_name]
113
+ opt_flag = h[:value]
114
+
115
+ # Call the FT setter with the value passed in
116
+ ft.method(ft_method).call(opt_flag)
117
+
118
+ msg += "* :#{opt_flag_name}=#{opt_flag} => ft.#{ft_method}(#{opt_flag})\n"
119
+ end
120
+
121
+ logger.info msg
122
+ end
123
+
124
+ model_idf = ft.translateModel(model)
106
125
  b = ::Time.now
107
126
  logger.info "Translate object to EnergyPlus IDF took #{b.to_f - a.to_f}"
108
127
  model_idf
109
128
  end
110
129
 
130
+ # Translates an IDF model into an EnergyPlus epJSON object
131
+ #
132
+ # @param [Object] OpenStudio::IdfFile instance to translate into an OpenStudio epJSON object -- see
133
+ # the OpenStudio SDK for details on the process
134
+ # @return [Object] Returns and OpenStudio::epJSONobject
135
+ #
136
+ def translate_idf_to_epjson(model_idf, logger = nil)
137
+ logger ||= ::Logger.new($stdout)
138
+ logger.info 'Translate IDF to epJSON in preparation for EnergyPlus'
139
+ a = ::Time.now
140
+ model_epjson = OpenStudio::EPJSON.toJSONString(model_idf)
141
+ b = ::Time.now
142
+ logger.info "Translate IDF to EnergyPlus epJSON took #{b.to_f - a.to_f}"
143
+
144
+ model_epjson
145
+ end
146
+
111
147
  # Saves an OpenStudio model object to file
112
148
  #
113
149
  # @param [Object] model The OpenStudio::Model instance to save to file
@@ -149,6 +185,27 @@ module OpenStudio
149
185
  end
150
186
  idf_filename
151
187
  end
188
+
189
+ # Saves an OpenStudio EpJSON model object to file
190
+ #
191
+ # @param [Object] model The OpenStudio::Workspace instance to save to file
192
+ # @param [String] save_directory Folder to save the model in
193
+ # @param [String] name ('in.epJSON') Option to define a non-standard name
194
+ # @return [String] epJSON file name
195
+ #
196
+ def save_epjson(model_epjson, save_directory, name = 'in.epJSON')
197
+ epjson_filename = File.join(save_directory.to_s, name.to_s)
198
+ File.open(epjson_filename, 'w') do |f|
199
+ f << model_epjson.to_s
200
+ # make sure data is written to the disk one way or the other
201
+ begin
202
+ f.fsync
203
+ rescue StandardError
204
+ f.flush
205
+ end
206
+ end
207
+ epjson_filename
208
+ end
152
209
  end
153
210
  end
154
211
  end
@@ -37,6 +37,6 @@
37
37
 
38
38
  module OpenStudio
39
39
  module Workflow
40
- VERSION = '2.2.1' # Suffixes must have periods (not dashes)
40
+ VERSION = '2.3.0' # Suffixes must have periods (not dashes)
41
41
  end
42
42
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openstudio-workflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicholas Long
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-06-02 00:00:00.000000000 Z
12
+ date: 2021-10-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: builder