openstudio-workflow 1.3.4 → 1.3.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +89 -77
- data/README.md +67 -93
- data/Rakefile +36 -36
- data/lib/openstudio-workflow.rb +65 -65
- data/lib/openstudio/workflow/adapters/input/local.rb +311 -324
- data/lib/openstudio/workflow/adapters/output/local.rb +158 -161
- data/lib/openstudio/workflow/adapters/output/socket.rb +106 -107
- data/lib/openstudio/workflow/adapters/output/web.rb +82 -82
- data/lib/openstudio/workflow/adapters/output_adapter.rb +163 -163
- data/lib/openstudio/workflow/job.rb +57 -57
- data/lib/openstudio/workflow/jobs/resources/monthly_report.idf +222 -222
- data/lib/openstudio/workflow/jobs/run_energyplus.rb +70 -70
- data/lib/openstudio/workflow/jobs/run_ep_measures.rb +73 -73
- data/lib/openstudio/workflow/jobs/run_initialization.rb +203 -203
- data/lib/openstudio/workflow/jobs/run_os_measures.rb +89 -89
- data/lib/openstudio/workflow/jobs/run_postprocess.rb +73 -73
- data/lib/openstudio/workflow/jobs/run_preprocess.rb +104 -104
- data/lib/openstudio/workflow/jobs/run_reporting_measures.rb +118 -118
- data/lib/openstudio/workflow/jobs/run_translation.rb +84 -84
- data/lib/openstudio/workflow/multi_delegator.rb +62 -62
- data/lib/openstudio/workflow/registry.rb +172 -172
- data/lib/openstudio/workflow/run.rb +342 -328
- data/lib/openstudio/workflow/time_logger.rb +96 -96
- data/lib/openstudio/workflow/util.rb +49 -49
- data/lib/openstudio/workflow/util/energyplus.rb +575 -605
- data/lib/openstudio/workflow/util/io.rb +68 -68
- data/lib/openstudio/workflow/util/measure.rb +658 -650
- data/lib/openstudio/workflow/util/model.rb +151 -151
- data/lib/openstudio/workflow/util/post_process.rb +235 -238
- data/lib/openstudio/workflow/util/weather_file.rb +143 -143
- data/lib/openstudio/workflow/version.rb +40 -40
- data/lib/openstudio/workflow_json.rb +475 -476
- data/lib/openstudio/workflow_runner.rb +263 -268
- metadata +24 -24
@@ -1,172 +1,172 @@
|
|
1
|
-
# *******************************************************************************
|
2
|
-
# OpenStudio(R), Copyright (c) 2008-
|
3
|
-
# All rights reserved.
|
4
|
-
# Redistribution and use in source and binary forms, with or without
|
5
|
-
# modification, are permitted provided that the following conditions are met:
|
6
|
-
#
|
7
|
-
# (1) Redistributions of source code must retain the above copyright notice,
|
8
|
-
# this list of conditions and the following disclaimer.
|
9
|
-
#
|
10
|
-
# (2) Redistributions in binary form must reproduce the above copyright notice,
|
11
|
-
# this list of conditions and the following disclaimer in the documentation
|
12
|
-
# and/or other materials provided with the distribution.
|
13
|
-
#
|
14
|
-
# (3) Neither the name of the copyright holder nor the names of any contributors
|
15
|
-
# may be used to endorse or promote products derived from this software without
|
16
|
-
# specific prior written permission from the respective party.
|
17
|
-
#
|
18
|
-
# (4) Other than as required in clauses (1) and (2), distributions in any form
|
19
|
-
# of modifications or other derivative works may not use the "OpenStudio"
|
20
|
-
# trademark, "OS", "os", or any other confusingly similar designation without
|
21
|
-
# specific prior written permission from Alliance for Sustainable Energy, LLC.
|
22
|
-
#
|
23
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
24
|
-
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
25
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
26
|
-
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER, THE UNITED STATES
|
27
|
-
# GOVERNMENT, OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
28
|
-
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
29
|
-
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
30
|
-
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
31
|
-
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
32
|
-
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
33
|
-
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
34
|
-
# *******************************************************************************
|
35
|
-
|
36
|
-
module OpenStudio
|
37
|
-
module Workflow
|
38
|
-
# Registers objects in a single place. Based on Hashicorps's Vagrant Registry class
|
39
|
-
#
|
40
|
-
# This allows certain components (such as models, weather files, proxy settings, etc.) to be registered as blocks
|
41
|
-
# and lazily updated. This allows for the state of various objects to be updated through evaluation of or
|
42
|
-
# overwriting the key. An instance of this class is passed between jobs to allow for highly flexible workflow
|
43
|
-
# definitions. Note that hashes can be passed into the registry as follows: hash = {...};
|
44
|
-
# Registry.new.register(:hash) { hash } or Registry.new.register(:hash) { {...} }. This class will likely absorb
|
45
|
-
# un-abstracted elements of the adapter class, see Workflow#Adapter
|
46
|
-
|
47
|
-
# @todo (rhorsey) registry should be a member of WorkflowRunner - DLM
|
48
|
-
# @todo (rhorsey) how is this different than a regular hash? why is it important to be able to register keys with blocks that return values instead of values, looks like the block is called on insert anyway? let's not go crazy on performance optimizations until we have to - DLM
|
49
|
-
class Registry
|
50
|
-
def initialize
|
51
|
-
@items = {}
|
52
|
-
@results_cache = {}
|
53
|
-
end
|
54
|
-
|
55
|
-
# Register a key and cache it's value. Note that if a key with the given name already exists it is overwritten
|
56
|
-
#
|
57
|
-
# @param [] key The key for the passed in block. Symbols are highly recommended
|
58
|
-
# @param [Proc] block The block (Proc) which contains the registered information
|
59
|
-
# @return [] Returns block.call from the registries cache
|
60
|
-
#
|
61
|
-
def register(key, &block)
|
62
|
-
raise ArgumentError, 'block required' unless block_given?
|
63
|
-
@items[key] = block
|
64
|
-
@results_cache[key] = @items[key].call
|
65
|
-
end
|
66
|
-
|
67
|
-
# Get the cached value of the given key
|
68
|
-
#
|
69
|
-
# @param [] key The key defining the block
|
70
|
-
# @return [] Returns the registries cached value for the key or nil if the key was not found
|
71
|
-
#
|
72
|
-
def get(key)
|
73
|
-
return nil unless @items.key?(key)
|
74
|
-
@results_cache[key]
|
75
|
-
end
|
76
|
-
alias [] get
|
77
|
-
|
78
|
-
# Re-evaluate the proc of a key and update the cache
|
79
|
-
#
|
80
|
-
# @param [Sym or String] key This will evaluate the item assigned to the key and update the cache if possible
|
81
|
-
# @return [] If successful the method returns the new value, and if it cannot find or cannot update the key it
|
82
|
-
# returns nil
|
83
|
-
#
|
84
|
-
def eval(key)
|
85
|
-
return nil unless @items.key?(key)
|
86
|
-
begin
|
87
|
-
@items[key].call
|
88
|
-
rescue
|
89
|
-
return nil
|
90
|
-
end
|
91
|
-
@results_cache[key] = @items[key].call
|
92
|
-
end
|
93
|
-
|
94
|
-
# Checks if the given key is registered with the registry
|
95
|
-
#
|
96
|
-
# @return [Boolean]
|
97
|
-
#
|
98
|
-
def key?(key)
|
99
|
-
@items.key?(key)
|
100
|
-
end
|
101
|
-
alias has_key? key?
|
102
|
-
|
103
|
-
# Returns an array populated with the keys of this object
|
104
|
-
#
|
105
|
-
# @return [Array]
|
106
|
-
#
|
107
|
-
def keys
|
108
|
-
@items.keys
|
109
|
-
end
|
110
|
-
|
111
|
-
# Return the number of elements in this registry
|
112
|
-
#
|
113
|
-
# @return [Fixnum]
|
114
|
-
#
|
115
|
-
def length
|
116
|
-
@items.keys.length
|
117
|
-
end
|
118
|
-
alias size length
|
119
|
-
|
120
|
-
# Checks if this registry has any items
|
121
|
-
#
|
122
|
-
# @return [Boolean]
|
123
|
-
#
|
124
|
-
def empty?
|
125
|
-
@items.keys.empty?
|
126
|
-
end
|
127
|
-
|
128
|
-
# Merge one registry with another and return a completely new registry. Note that the result cache is completely
|
129
|
-
# busted, so any gets on the new registry will result in a cache miss
|
130
|
-
#
|
131
|
-
# @param [Registry] other The other #Registry to merge onto of self
|
132
|
-
# @return [Registry] A merged #Registry
|
133
|
-
#
|
134
|
-
def merge(other)
|
135
|
-
self.class.new.tap do |result|
|
136
|
-
result.merge!(self)
|
137
|
-
result.merge!(other)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
# Like #merge but updates self
|
142
|
-
#
|
143
|
-
# @param [Registry] other The other #Registry to merge onto of self
|
144
|
-
# @return [Void]
|
145
|
-
#
|
146
|
-
def merge!(other)
|
147
|
-
@items.merge!(other.__internal_state[:items])
|
148
|
-
self
|
149
|
-
end
|
150
|
-
|
151
|
-
# Converts the registry to a hash
|
152
|
-
#
|
153
|
-
# @return [Hash] The registry as a hash
|
154
|
-
#
|
155
|
-
def to_hash
|
156
|
-
result = {}
|
157
|
-
@results_cache.each_pair do |key, value|
|
158
|
-
result[key] = value
|
159
|
-
end
|
160
|
-
|
161
|
-
result
|
162
|
-
end
|
163
|
-
|
164
|
-
def __internal_state
|
165
|
-
{
|
166
|
-
items: @items,
|
167
|
-
results_cache: @results_cache
|
168
|
-
}
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
1
|
+
# *******************************************************************************
|
2
|
+
# OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
|
3
|
+
# All rights reserved.
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# (1) Redistributions of source code must retain the above copyright notice,
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
#
|
10
|
+
# (2) Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
#
|
14
|
+
# (3) Neither the name of the copyright holder nor the names of any contributors
|
15
|
+
# may be used to endorse or promote products derived from this software without
|
16
|
+
# specific prior written permission from the respective party.
|
17
|
+
#
|
18
|
+
# (4) Other than as required in clauses (1) and (2), distributions in any form
|
19
|
+
# of modifications or other derivative works may not use the "OpenStudio"
|
20
|
+
# trademark, "OS", "os", or any other confusingly similar designation without
|
21
|
+
# specific prior written permission from Alliance for Sustainable Energy, LLC.
|
22
|
+
#
|
23
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
24
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
25
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
26
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER, THE UNITED STATES
|
27
|
+
# GOVERNMENT, OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
28
|
+
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
29
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
30
|
+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
31
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
32
|
+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
33
|
+
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
34
|
+
# *******************************************************************************
|
35
|
+
|
36
|
+
module OpenStudio
|
37
|
+
module Workflow
|
38
|
+
# Registers objects in a single place. Based on Hashicorps's Vagrant Registry class
|
39
|
+
#
|
40
|
+
# This allows certain components (such as models, weather files, proxy settings, etc.) to be registered as blocks
|
41
|
+
# and lazily updated. This allows for the state of various objects to be updated through evaluation of or
|
42
|
+
# overwriting the key. An instance of this class is passed between jobs to allow for highly flexible workflow
|
43
|
+
# definitions. Note that hashes can be passed into the registry as follows: hash = {...};
|
44
|
+
# Registry.new.register(:hash) { hash } or Registry.new.register(:hash) { {...} }. This class will likely absorb
|
45
|
+
# un-abstracted elements of the adapter class, see Workflow#Adapter
|
46
|
+
|
47
|
+
# @todo (rhorsey) registry should be a member of WorkflowRunner - DLM
|
48
|
+
# @todo (rhorsey) how is this different than a regular hash? why is it important to be able to register keys with blocks that return values instead of values, looks like the block is called on insert anyway? let's not go crazy on performance optimizations until we have to - DLM
|
49
|
+
class Registry
|
50
|
+
def initialize
|
51
|
+
@items = {}
|
52
|
+
@results_cache = {}
|
53
|
+
end
|
54
|
+
|
55
|
+
# Register a key and cache it's value. Note that if a key with the given name already exists it is overwritten
|
56
|
+
#
|
57
|
+
# @param [] key The key for the passed in block. Symbols are highly recommended
|
58
|
+
# @param [Proc] block The block (Proc) which contains the registered information
|
59
|
+
# @return [] Returns block.call from the registries cache
|
60
|
+
#
|
61
|
+
def register(key, &block)
|
62
|
+
raise ArgumentError, 'block required' unless block_given?
|
63
|
+
@items[key] = block
|
64
|
+
@results_cache[key] = @items[key].call
|
65
|
+
end
|
66
|
+
|
67
|
+
# Get the cached value of the given key
|
68
|
+
#
|
69
|
+
# @param [] key The key defining the block
|
70
|
+
# @return [] Returns the registries cached value for the key or nil if the key was not found
|
71
|
+
#
|
72
|
+
def get(key)
|
73
|
+
return nil unless @items.key?(key)
|
74
|
+
@results_cache[key]
|
75
|
+
end
|
76
|
+
alias [] get
|
77
|
+
|
78
|
+
# Re-evaluate the proc of a key and update the cache
|
79
|
+
#
|
80
|
+
# @param [Sym or String] key This will evaluate the item assigned to the key and update the cache if possible
|
81
|
+
# @return [] If successful the method returns the new value, and if it cannot find or cannot update the key it
|
82
|
+
# returns nil
|
83
|
+
#
|
84
|
+
def eval(key)
|
85
|
+
return nil unless @items.key?(key)
|
86
|
+
begin
|
87
|
+
@items[key].call
|
88
|
+
rescue StandardError
|
89
|
+
return nil
|
90
|
+
end
|
91
|
+
@results_cache[key] = @items[key].call
|
92
|
+
end
|
93
|
+
|
94
|
+
# Checks if the given key is registered with the registry
|
95
|
+
#
|
96
|
+
# @return [Boolean]
|
97
|
+
#
|
98
|
+
def key?(key)
|
99
|
+
@items.key?(key)
|
100
|
+
end
|
101
|
+
alias has_key? key?
|
102
|
+
|
103
|
+
# Returns an array populated with the keys of this object
|
104
|
+
#
|
105
|
+
# @return [Array]
|
106
|
+
#
|
107
|
+
def keys
|
108
|
+
@items.keys
|
109
|
+
end
|
110
|
+
|
111
|
+
# Return the number of elements in this registry
|
112
|
+
#
|
113
|
+
# @return [Fixnum]
|
114
|
+
#
|
115
|
+
def length
|
116
|
+
@items.keys.length
|
117
|
+
end
|
118
|
+
alias size length
|
119
|
+
|
120
|
+
# Checks if this registry has any items
|
121
|
+
#
|
122
|
+
# @return [Boolean]
|
123
|
+
#
|
124
|
+
def empty?
|
125
|
+
@items.keys.empty?
|
126
|
+
end
|
127
|
+
|
128
|
+
# Merge one registry with another and return a completely new registry. Note that the result cache is completely
|
129
|
+
# busted, so any gets on the new registry will result in a cache miss
|
130
|
+
#
|
131
|
+
# @param [Registry] other The other #Registry to merge onto of self
|
132
|
+
# @return [Registry] A merged #Registry
|
133
|
+
#
|
134
|
+
def merge(other)
|
135
|
+
self.class.new.tap do |result|
|
136
|
+
result.merge!(self)
|
137
|
+
result.merge!(other)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Like #merge but updates self
|
142
|
+
#
|
143
|
+
# @param [Registry] other The other #Registry to merge onto of self
|
144
|
+
# @return [Void]
|
145
|
+
#
|
146
|
+
def merge!(other)
|
147
|
+
@items.merge!(other.__internal_state[:items])
|
148
|
+
self
|
149
|
+
end
|
150
|
+
|
151
|
+
# Converts the registry to a hash
|
152
|
+
#
|
153
|
+
# @return [Hash] The registry as a hash
|
154
|
+
#
|
155
|
+
def to_hash
|
156
|
+
result = {}
|
157
|
+
@results_cache.each_pair do |key, value|
|
158
|
+
result[key] = value
|
159
|
+
end
|
160
|
+
|
161
|
+
result
|
162
|
+
end
|
163
|
+
|
164
|
+
def __internal_state
|
165
|
+
{
|
166
|
+
items: @items,
|
167
|
+
results_cache: @results_cache
|
168
|
+
}
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -1,328 +1,342 @@
|
|
1
|
-
# *******************************************************************************
|
2
|
-
# OpenStudio(R), Copyright (c) 2008-
|
3
|
-
# All rights reserved.
|
4
|
-
# Redistribution and use in source and binary forms, with or without
|
5
|
-
# modification, are permitted provided that the following conditions are met:
|
6
|
-
#
|
7
|
-
# (1) Redistributions of source code must retain the above copyright notice,
|
8
|
-
# this list of conditions and the following disclaimer.
|
9
|
-
#
|
10
|
-
# (2) Redistributions in binary form must reproduce the above copyright notice,
|
11
|
-
# this list of conditions and the following disclaimer in the documentation
|
12
|
-
# and/or other materials provided with the distribution.
|
13
|
-
#
|
14
|
-
# (3) Neither the name of the copyright holder nor the names of any contributors
|
15
|
-
# may be used to endorse or promote products derived from this software without
|
16
|
-
# specific prior written permission from the respective party.
|
17
|
-
#
|
18
|
-
# (4) Other than as required in clauses (1) and (2), distributions in any form
|
19
|
-
# of modifications or other derivative works may not use the "OpenStudio"
|
20
|
-
# trademark, "OS", "os", or any other confusingly similar designation without
|
21
|
-
# specific prior written permission from Alliance for Sustainable Energy, LLC.
|
22
|
-
#
|
23
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
24
|
-
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
25
|
-
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
26
|
-
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER, THE UNITED STATES
|
27
|
-
# GOVERNMENT, OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
28
|
-
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
29
|
-
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
30
|
-
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
31
|
-
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
32
|
-
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
33
|
-
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
34
|
-
# *******************************************************************************
|
35
|
-
|
36
|
-
require_relative 'registry'
|
37
|
-
require_relative 'adapters/input/local'
|
38
|
-
require_relative 'adapters/output/local'
|
39
|
-
|
40
|
-
require 'logger'
|
41
|
-
require 'pathname'
|
42
|
-
|
43
|
-
# Run Class for OpenStudio workflow. All comments here need some love, as well as the code itself
|
44
|
-
module OpenStudio
|
45
|
-
module Workflow
|
46
|
-
class Run
|
47
|
-
attr_accessor :registry
|
48
|
-
|
49
|
-
attr_reader :options
|
50
|
-
attr_reader :input_adapter
|
51
|
-
attr_reader :output_adapter
|
52
|
-
attr_reader :final_message
|
53
|
-
attr_reader :job_results
|
54
|
-
attr_reader :current_state
|
55
|
-
|
56
|
-
# Define the default set of jobs. Note that the states of :queued of :finished need to exist for all job arrays.
|
57
|
-
#
|
58
|
-
def self.default_jobs
|
59
|
-
[
|
60
|
-
{ state: :queued, next_state: :initialization, options: { initial: true } },
|
61
|
-
{ state: :initialization, next_state: :os_measures, job: :RunInitialization,
|
62
|
-
file: 'openstudio/workflow/jobs/run_initialization', options: {} },
|
63
|
-
{ state: :os_measures, next_state: :translator, job: :RunOpenStudioMeasures,
|
64
|
-
file: 'openstudio/workflow/jobs/run_os_measures.rb', options: {} },
|
65
|
-
{ state: :translator, next_state: :ep_measures, job: :RunTranslation,
|
66
|
-
file: 'openstudio/workflow/jobs/run_translation.rb', options: {} },
|
67
|
-
{ state: :ep_measures, next_state: :preprocess, job: :RunEnergyPlusMeasures,
|
68
|
-
file: 'openstudio/workflow/jobs/run_ep_measures.rb', options: {} },
|
69
|
-
{ state: :preprocess, next_state: :simulation, job: :RunPreprocess,
|
70
|
-
file: 'openstudio/workflow/jobs/run_preprocess.rb', options: {} },
|
71
|
-
{ state: :simulation, next_state: :reporting_measures, job: :RunEnergyPlus,
|
72
|
-
file: 'openstudio/workflow/jobs/run_energyplus.rb', options: {} },
|
73
|
-
{ state: :reporting_measures, next_state: :postprocess, job: :RunReportingMeasures,
|
74
|
-
file: 'openstudio/workflow/jobs/run_reporting_measures.rb', options: {} },
|
75
|
-
{ state: :postprocess, next_state: :finished, job: :RunPostprocess,
|
76
|
-
file: 'openstudio/workflow/jobs/run_postprocess.rb', options: {} },
|
77
|
-
{ state: :finished },
|
78
|
-
{ state: :errored }
|
79
|
-
]
|
80
|
-
end
|
81
|
-
|
82
|
-
# Initialize a new run class
|
83
|
-
#
|
84
|
-
# @param [String] osw_path the path to the OSW file to run. It is highly recommended that this be an absolute
|
85
|
-
# path, however if not it will be made absolute relative to the current working directory
|
86
|
-
# @param [Hash] user_options ({}) A set of user-specified options that are used to override default behaviors.
|
87
|
-
# @option user_options [Hash] :cleanup Remove unneccessary files during post processing, overrides OSW option if set, defaults to true
|
88
|
-
# @option user_options [Hash] :debug Print debugging messages, overrides OSW option if set, defaults to false
|
89
|
-
# @option user_options [Hash] :energyplus_path Specifies path to energyplus executable, defaults to empty
|
90
|
-
# @option user_options [Hash] :fast Speeds up workflow by skipping steps not needed for running simulations, defaults to false
|
91
|
-
# @option user_options [Hash] :jobs Simulation workflow, overrides jobs in OSW if set, defaults to default_jobs
|
92
|
-
# @option user_options [Hash] :output_adapter Output adapter to use, overrides output adapter in OSW if set, defaults to local adapter
|
93
|
-
# @option user_options [Hash] :preserve_run_dir Prevents run directory from being cleaned prior to run, overrides OSW option if set, defaults to false - DLM, Deprecate
|
94
|
-
# @option user_options [Hash] :profile Produce additional output for profiling simulations, defaults to false
|
95
|
-
# @option user_options [Hash] :skip_energyplus_preprocess Does not add add default output requests to EnergyPlus input if true, requests from reporting measures are added in either case, defaults to false
|
96
|
-
# @option user_options [Hash] :skip_expand_objects Skips the call to the EnergyPlus ExpandObjects program, defaults to false
|
97
|
-
# @option user_options [Hash] :targets Log targets to write to, defaults to standard out and run.log
|
98
|
-
# @option user_options [Hash] :verify_osw Check OSW for correctness, defaults to true
|
99
|
-
# @option user_options [Hash] :weather_file Initial weather file to load, overrides OSW option if set, defaults to empty
|
100
|
-
def initialize(osw_path, user_options = {})
|
101
|
-
# DLM - what is final_message?
|
102
|
-
@final_message = ''
|
103
|
-
@current_state = nil
|
104
|
-
@options = {}
|
105
|
-
|
106
|
-
# Registry is a large hash of objects that are populated during the run, the number of objects in the registry should be reduced over time, especially if the functionality can be added to the WorkflowJSON class
|
107
|
-
# - analysis - the current OSA parsed as a Ruby Hash
|
108
|
-
# - datapoint - the current OSD parsed as a Ruby Hash
|
109
|
-
# - log_targets - IO devices that are being logged to
|
110
|
-
# - logger - general logger
|
111
|
-
# - model - the current OpenStudio Model object, updated after each step
|
112
|
-
# - model_idf - the current EnergyPlus Workspace object, updated after each step
|
113
|
-
# - openstudio_2 - true if we are running in OpenStudio 2.X environment
|
114
|
-
# - osw_path - the path the OSW was loaded from as a string
|
115
|
-
# - osw_dir - the directory the OSW was loaded from as a string
|
116
|
-
# - output_attributes - added during simulation time
|
117
|
-
# - results - objective function values
|
118
|
-
# - root_dir - the root directory in the OSW as a string
|
119
|
-
# - run_dir - the run directory for the simulation as a string
|
120
|
-
# - runner - the current OSRunner object
|
121
|
-
# - sql - the path to the current EnergyPlus SQL file as a string
|
122
|
-
# - time_logger - logger for doing profiling - time to run each step will be captured in OSResult, deprecate
|
123
|
-
# - wf - the path to the current weather file as a string, updated after each step
|
124
|
-
# - workflow - the current OSW parsed as a Ruby Hash
|
125
|
-
# - workflow_json - the current WorkflowJSON object
|
126
|
-
@registry = Registry.new
|
127
|
-
|
128
|
-
openstudio_2 = false
|
129
|
-
begin
|
130
|
-
# OpenStudio 2.X test
|
131
|
-
OpenStudio::WorkflowJSON.new
|
132
|
-
openstudio_2 = true
|
133
|
-
rescue NameError => e
|
134
|
-
end
|
135
|
-
@registry.register(:openstudio_2) { openstudio_2 }
|
136
|
-
|
137
|
-
# get the input osw
|
138
|
-
@input_adapter = OpenStudio::Workflow::InputAdapter::Local.new(osw_path)
|
139
|
-
|
140
|
-
# DLM: need to check that we have correct permissions to all these paths
|
141
|
-
@registry.register(:osw_path) { Pathname.new(@input_adapter.osw_path).realpath }
|
142
|
-
@registry.register(:osw_dir) { Pathname.new(@input_adapter.osw_dir).realpath }
|
143
|
-
@registry.register(:run_dir) { Pathname.new(@input_adapter.run_dir).cleanpath } # run dir might not yet exist, calling realpath will throw
|
144
|
-
|
145
|
-
# get info to set up logging first in case of failures later
|
146
|
-
@options[:debug] = @input_adapter.debug(user_options, false)
|
147
|
-
@options[:preserve_run_dir] = @input_adapter.preserve_run_dir(user_options, false)
|
148
|
-
@options[:skip_expand_objects] = @input_adapter.skip_expand_objects(user_options, false)
|
149
|
-
@options[:skip_energyplus_preprocess] = @input_adapter.skip_energyplus_preprocess(user_options, false)
|
150
|
-
@options[:profile] = @input_adapter.profile(user_options, false)
|
151
|
-
|
152
|
-
# if running in osw dir, force preserve run dir
|
153
|
-
if @registry[:osw_dir] == @registry[:run_dir]
|
154
|
-
# force preserving the run directory
|
155
|
-
@options[:preserve_run_dir] = true
|
156
|
-
end
|
157
|
-
|
158
|
-
# By default blow away the entire run directory every time and recreate it
|
159
|
-
unless @options[:preserve_run_dir]
|
160
|
-
if File.exist?(@registry[:run_dir])
|
161
|
-
# logger is not initialized yet (it needs run dir to exist for log)
|
162
|
-
puts "Removing existing run directory #{@registry[:run_dir]}" if @options[:debug]
|
163
|
-
|
164
|
-
# DLM: this is dangerous, we are calling rm_rf on a user entered directory, need to check this first
|
165
|
-
# TODO: Echoing Dan's comment
|
166
|
-
FileUtils.rm_rf(@registry[:run_dir])
|
167
|
-
end
|
168
|
-
end
|
169
|
-
FileUtils.mkdir_p(@registry[:run_dir])
|
170
|
-
|
171
|
-
# set up logging after cleaning run dir
|
172
|
-
if user_options[:targets]
|
173
|
-
@options[:targets] = user_options[:targets]
|
174
|
-
else
|
175
|
-
# don't create these files unless we want to use them
|
176
|
-
# DLM: TODO, make sure that run.log will be closed later
|
177
|
-
@options[:targets] = [STDOUT, File.open(File.join(@registry[:run_dir], 'run.log'), 'a')]
|
178
|
-
end
|
179
|
-
|
180
|
-
@registry.register(:log_targets) { @options[:targets] }
|
181
|
-
@registry.register(:time_logger) { TimeLogger.new } if @options[:profile]
|
182
|
-
|
183
|
-
# Initialize the MultiDelegator logger
|
184
|
-
logger_level = @options[:debug] ? ::Logger::DEBUG : ::Logger::WARN
|
185
|
-
@logger = ::Logger.new(MultiDelegator.delegate(:write, :close).to(*@options[:targets])) # * is the splat operator
|
186
|
-
@logger.level = logger_level
|
187
|
-
@registry.register(:logger) { @logger }
|
188
|
-
|
189
|
-
@logger.info "openstudio_2 = #{@registry[:openstudio_2]}"
|
190
|
-
|
191
|
-
# get the output adapter
|
192
|
-
default_output_adapter = OpenStudio::Workflow::OutputAdapter::Local.new(output_directory: @input_adapter.run_dir)
|
193
|
-
@output_adapter = @input_adapter.output_adapter(user_options, default_output_adapter, @logger)
|
194
|
-
|
195
|
-
# get the jobs
|
196
|
-
default_jobs = OpenStudio::Workflow::Run.default_jobs
|
197
|
-
@jobs = @input_adapter.jobs(user_options, default_jobs, @logger)
|
198
|
-
|
199
|
-
# get other run options out of user_options and into permanent options
|
200
|
-
@options[:cleanup] = @input_adapter.cleanup(user_options, true)
|
201
|
-
@options[:energyplus_path] = @input_adapter.energyplus_path(user_options, nil)
|
202
|
-
@options[:verify_osw] = @input_adapter.verify_osw(user_options, true)
|
203
|
-
@options[:weather_file] = @input_adapter.weather_file(user_options, nil)
|
204
|
-
@options[:fast] = @input_adapter.fast(user_options, false)
|
205
|
-
|
206
|
-
openstudio_dir =
|
207
|
-
begin
|
208
|
-
openstudio_dir = $OpenStudio_Dir
|
209
|
-
if openstudio_dir.nil?
|
210
|
-
openstudio_dir = OpenStudio
|
211
|
-
end
|
212
|
-
rescue
|
213
|
-
end
|
214
|
-
@logger.info "openstudio_dir = #{openstudio_dir}"
|
215
|
-
|
216
|
-
@logger.info "Initializing directory #{@registry[:run_dir]} for simulation with options #{@options}"
|
217
|
-
|
218
|
-
# Define the state and transitions
|
219
|
-
@current_state = :queued
|
220
|
-
end
|
221
|
-
|
222
|
-
# execute the workflow defined in the state object
|
223
|
-
#
|
224
|
-
# @todo add a catch if any job fails
|
225
|
-
# @todo make a block method to provide feedback
|
226
|
-
def run
|
227
|
-
@logger.info "Starting workflow in #{@registry[:run_dir]}"
|
228
|
-
begin
|
229
|
-
next_state
|
230
|
-
while @current_state != :finished && @current_state != :errored
|
231
|
-
#sleep 2
|
232
|
-
step
|
233
|
-
end
|
234
|
-
|
235
|
-
if !@options[:fast]
|
236
|
-
@logger.info 'Finished workflow - communicating results and zipping files'
|
237
|
-
@output_adapter.communicate_results(@registry[:run_dir], @registry[:results])
|
238
|
-
end
|
239
|
-
rescue => e
|
240
|
-
@logger.info "Error occurred during running with #{e.message}"
|
241
|
-
ensure
|
242
|
-
@logger.info 'Workflow complete'
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
#
|
265
|
-
@registry[:
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
#
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
end
|
1
|
+
# *******************************************************************************
|
2
|
+
# OpenStudio(R), Copyright (c) 2008-2020, Alliance for Sustainable Energy, LLC.
|
3
|
+
# All rights reserved.
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# (1) Redistributions of source code must retain the above copyright notice,
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
#
|
10
|
+
# (2) Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
#
|
14
|
+
# (3) Neither the name of the copyright holder nor the names of any contributors
|
15
|
+
# may be used to endorse or promote products derived from this software without
|
16
|
+
# specific prior written permission from the respective party.
|
17
|
+
#
|
18
|
+
# (4) Other than as required in clauses (1) and (2), distributions in any form
|
19
|
+
# of modifications or other derivative works may not use the "OpenStudio"
|
20
|
+
# trademark, "OS", "os", or any other confusingly similar designation without
|
21
|
+
# specific prior written permission from Alliance for Sustainable Energy, LLC.
|
22
|
+
#
|
23
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
24
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
25
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
26
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER, THE UNITED STATES
|
27
|
+
# GOVERNMENT, OR ANY CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
28
|
+
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
29
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
30
|
+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
31
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
32
|
+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
33
|
+
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
34
|
+
# *******************************************************************************
|
35
|
+
|
36
|
+
require_relative 'registry'
|
37
|
+
require_relative 'adapters/input/local'
|
38
|
+
require_relative 'adapters/output/local'
|
39
|
+
|
40
|
+
require 'logger'
|
41
|
+
require 'pathname'
|
42
|
+
|
43
|
+
# Run Class for OpenStudio workflow. All comments here need some love, as well as the code itself
|
44
|
+
module OpenStudio
|
45
|
+
module Workflow
|
46
|
+
class Run
|
47
|
+
attr_accessor :registry
|
48
|
+
|
49
|
+
attr_reader :options
|
50
|
+
attr_reader :input_adapter
|
51
|
+
attr_reader :output_adapter
|
52
|
+
attr_reader :final_message
|
53
|
+
attr_reader :job_results
|
54
|
+
attr_reader :current_state
|
55
|
+
|
56
|
+
# Define the default set of jobs. Note that the states of :queued of :finished need to exist for all job arrays.
|
57
|
+
#
|
58
|
+
def self.default_jobs
|
59
|
+
[
|
60
|
+
{ state: :queued, next_state: :initialization, options: { initial: true } },
|
61
|
+
{ state: :initialization, next_state: :os_measures, job: :RunInitialization,
|
62
|
+
file: 'openstudio/workflow/jobs/run_initialization', options: {} },
|
63
|
+
{ state: :os_measures, next_state: :translator, job: :RunOpenStudioMeasures,
|
64
|
+
file: 'openstudio/workflow/jobs/run_os_measures.rb', options: {} },
|
65
|
+
{ state: :translator, next_state: :ep_measures, job: :RunTranslation,
|
66
|
+
file: 'openstudio/workflow/jobs/run_translation.rb', options: {} },
|
67
|
+
{ state: :ep_measures, next_state: :preprocess, job: :RunEnergyPlusMeasures,
|
68
|
+
file: 'openstudio/workflow/jobs/run_ep_measures.rb', options: {} },
|
69
|
+
{ state: :preprocess, next_state: :simulation, job: :RunPreprocess,
|
70
|
+
file: 'openstudio/workflow/jobs/run_preprocess.rb', options: {} },
|
71
|
+
{ state: :simulation, next_state: :reporting_measures, job: :RunEnergyPlus,
|
72
|
+
file: 'openstudio/workflow/jobs/run_energyplus.rb', options: {} },
|
73
|
+
{ state: :reporting_measures, next_state: :postprocess, job: :RunReportingMeasures,
|
74
|
+
file: 'openstudio/workflow/jobs/run_reporting_measures.rb', options: {} },
|
75
|
+
{ state: :postprocess, next_state: :finished, job: :RunPostprocess,
|
76
|
+
file: 'openstudio/workflow/jobs/run_postprocess.rb', options: {} },
|
77
|
+
{ state: :finished },
|
78
|
+
{ state: :errored }
|
79
|
+
]
|
80
|
+
end
|
81
|
+
|
82
|
+
# Initialize a new run class
|
83
|
+
#
|
84
|
+
# @param [String] osw_path the path to the OSW file to run. It is highly recommended that this be an absolute
|
85
|
+
# path, however if not it will be made absolute relative to the current working directory
|
86
|
+
# @param [Hash] user_options ({}) A set of user-specified options that are used to override default behaviors.
|
87
|
+
# @option user_options [Hash] :cleanup Remove unneccessary files during post processing, overrides OSW option if set, defaults to true
|
88
|
+
# @option user_options [Hash] :debug Print debugging messages, overrides OSW option if set, defaults to false
|
89
|
+
# @option user_options [Hash] :energyplus_path Specifies path to energyplus executable, defaults to empty
|
90
|
+
# @option user_options [Hash] :fast Speeds up workflow by skipping steps not needed for running simulations, defaults to false
|
91
|
+
# @option user_options [Hash] :jobs Simulation workflow, overrides jobs in OSW if set, defaults to default_jobs
|
92
|
+
# @option user_options [Hash] :output_adapter Output adapter to use, overrides output adapter in OSW if set, defaults to local adapter
|
93
|
+
# @option user_options [Hash] :preserve_run_dir Prevents run directory from being cleaned prior to run, overrides OSW option if set, defaults to false - DLM, Deprecate
|
94
|
+
# @option user_options [Hash] :profile Produce additional output for profiling simulations, defaults to false
|
95
|
+
# @option user_options [Hash] :skip_energyplus_preprocess Does not add add default output requests to EnergyPlus input if true, requests from reporting measures are added in either case, defaults to false
|
96
|
+
# @option user_options [Hash] :skip_expand_objects Skips the call to the EnergyPlus ExpandObjects program, defaults to false
|
97
|
+
# @option user_options [Hash] :targets Log targets to write to, defaults to standard out and run.log
|
98
|
+
# @option user_options [Hash] :verify_osw Check OSW for correctness, defaults to true
|
99
|
+
# @option user_options [Hash] :weather_file Initial weather file to load, overrides OSW option if set, defaults to empty
|
100
|
+
def initialize(osw_path, user_options = {})
|
101
|
+
# DLM - what is final_message?
|
102
|
+
@final_message = ''
|
103
|
+
@current_state = nil
|
104
|
+
@options = {}
|
105
|
+
|
106
|
+
# Registry is a large hash of objects that are populated during the run, the number of objects in the registry should be reduced over time, especially if the functionality can be added to the WorkflowJSON class
|
107
|
+
# - analysis - the current OSA parsed as a Ruby Hash
|
108
|
+
# - datapoint - the current OSD parsed as a Ruby Hash
|
109
|
+
# - log_targets - IO devices that are being logged to
|
110
|
+
# - logger - general logger
|
111
|
+
# - model - the current OpenStudio Model object, updated after each step
|
112
|
+
# - model_idf - the current EnergyPlus Workspace object, updated after each step
|
113
|
+
# - openstudio_2 - true if we are running in OpenStudio 2.X environment
|
114
|
+
# - osw_path - the path the OSW was loaded from as a string
|
115
|
+
# - osw_dir - the directory the OSW was loaded from as a string
|
116
|
+
# - output_attributes - added during simulation time
|
117
|
+
# - results - objective function values
|
118
|
+
# - root_dir - the root directory in the OSW as a string
|
119
|
+
# - run_dir - the run directory for the simulation as a string
|
120
|
+
# - runner - the current OSRunner object
|
121
|
+
# - sql - the path to the current EnergyPlus SQL file as a string
|
122
|
+
# - time_logger - logger for doing profiling - time to run each step will be captured in OSResult, deprecate
|
123
|
+
# - wf - the path to the current weather file as a string, updated after each step
|
124
|
+
# - workflow - the current OSW parsed as a Ruby Hash
|
125
|
+
# - workflow_json - the current WorkflowJSON object
|
126
|
+
@registry = Registry.new
|
127
|
+
|
128
|
+
openstudio_2 = false
|
129
|
+
begin
|
130
|
+
# OpenStudio 2.X test
|
131
|
+
OpenStudio::WorkflowJSON.new
|
132
|
+
openstudio_2 = true
|
133
|
+
rescue NameError => e
|
134
|
+
end
|
135
|
+
@registry.register(:openstudio_2) { openstudio_2 }
|
136
|
+
|
137
|
+
# get the input osw
|
138
|
+
@input_adapter = OpenStudio::Workflow::InputAdapter::Local.new(osw_path)
|
139
|
+
|
140
|
+
# DLM: need to check that we have correct permissions to all these paths
|
141
|
+
@registry.register(:osw_path) { Pathname.new(@input_adapter.osw_path).realpath }
|
142
|
+
@registry.register(:osw_dir) { Pathname.new(@input_adapter.osw_dir).realpath }
|
143
|
+
@registry.register(:run_dir) { Pathname.new(@input_adapter.run_dir).cleanpath } # run dir might not yet exist, calling realpath will throw
|
144
|
+
|
145
|
+
# get info to set up logging first in case of failures later
|
146
|
+
@options[:debug] = @input_adapter.debug(user_options, false)
|
147
|
+
@options[:preserve_run_dir] = @input_adapter.preserve_run_dir(user_options, false)
|
148
|
+
@options[:skip_expand_objects] = @input_adapter.skip_expand_objects(user_options, false)
|
149
|
+
@options[:skip_energyplus_preprocess] = @input_adapter.skip_energyplus_preprocess(user_options, false)
|
150
|
+
@options[:profile] = @input_adapter.profile(user_options, false)
|
151
|
+
|
152
|
+
# if running in osw dir, force preserve run dir
|
153
|
+
if @registry[:osw_dir] == @registry[:run_dir]
|
154
|
+
# force preserving the run directory
|
155
|
+
@options[:preserve_run_dir] = true
|
156
|
+
end
|
157
|
+
|
158
|
+
# By default blow away the entire run directory every time and recreate it
|
159
|
+
unless @options[:preserve_run_dir]
|
160
|
+
if File.exist?(@registry[:run_dir])
|
161
|
+
# logger is not initialized yet (it needs run dir to exist for log)
|
162
|
+
puts "Removing existing run directory #{@registry[:run_dir]}" if @options[:debug]
|
163
|
+
|
164
|
+
# DLM: this is dangerous, we are calling rm_rf on a user entered directory, need to check this first
|
165
|
+
# TODO: Echoing Dan's comment
|
166
|
+
FileUtils.rm_rf(@registry[:run_dir])
|
167
|
+
end
|
168
|
+
end
|
169
|
+
FileUtils.mkdir_p(@registry[:run_dir])
|
170
|
+
|
171
|
+
# set up logging after cleaning run dir
|
172
|
+
if user_options[:targets]
|
173
|
+
@options[:targets] = user_options[:targets]
|
174
|
+
else
|
175
|
+
# don't create these files unless we want to use them
|
176
|
+
# DLM: TODO, make sure that run.log will be closed later
|
177
|
+
@options[:targets] = [STDOUT, File.open(File.join(@registry[:run_dir], 'run.log'), 'a')]
|
178
|
+
end
|
179
|
+
|
180
|
+
@registry.register(:log_targets) { @options[:targets] }
|
181
|
+
@registry.register(:time_logger) { TimeLogger.new } if @options[:profile]
|
182
|
+
|
183
|
+
# Initialize the MultiDelegator logger
|
184
|
+
logger_level = @options[:debug] ? ::Logger::DEBUG : ::Logger::WARN
|
185
|
+
@logger = ::Logger.new(MultiDelegator.delegate(:write, :close).to(*@options[:targets])) # * is the splat operator
|
186
|
+
@logger.level = logger_level
|
187
|
+
@registry.register(:logger) { @logger }
|
188
|
+
|
189
|
+
@logger.info "openstudio_2 = #{@registry[:openstudio_2]}"
|
190
|
+
|
191
|
+
# get the output adapter
|
192
|
+
default_output_adapter = OpenStudio::Workflow::OutputAdapter::Local.new(output_directory: @input_adapter.run_dir)
|
193
|
+
@output_adapter = @input_adapter.output_adapter(user_options, default_output_adapter, @logger)
|
194
|
+
|
195
|
+
# get the jobs
|
196
|
+
default_jobs = OpenStudio::Workflow::Run.default_jobs
|
197
|
+
@jobs = @input_adapter.jobs(user_options, default_jobs, @logger)
|
198
|
+
|
199
|
+
# get other run options out of user_options and into permanent options
|
200
|
+
@options[:cleanup] = @input_adapter.cleanup(user_options, true)
|
201
|
+
@options[:energyplus_path] = @input_adapter.energyplus_path(user_options, nil)
|
202
|
+
@options[:verify_osw] = @input_adapter.verify_osw(user_options, true)
|
203
|
+
@options[:weather_file] = @input_adapter.weather_file(user_options, nil)
|
204
|
+
@options[:fast] = @input_adapter.fast(user_options, false)
|
205
|
+
|
206
|
+
openstudio_dir = 'unknown'
|
207
|
+
begin
|
208
|
+
openstudio_dir = $OpenStudio_Dir
|
209
|
+
if openstudio_dir.nil?
|
210
|
+
openstudio_dir = OpenStudio.getOpenStudioModuleDirectory.to_s
|
211
|
+
end
|
212
|
+
rescue StandardError
|
213
|
+
end
|
214
|
+
@logger.info "openstudio_dir = #{openstudio_dir}"
|
215
|
+
|
216
|
+
@logger.info "Initializing directory #{@registry[:run_dir]} for simulation with options #{@options}"
|
217
|
+
|
218
|
+
# Define the state and transitions
|
219
|
+
@current_state = :queued
|
220
|
+
end
|
221
|
+
|
222
|
+
# execute the workflow defined in the state object
|
223
|
+
#
|
224
|
+
# @todo add a catch if any job fails
|
225
|
+
# @todo make a block method to provide feedback
|
226
|
+
def run
|
227
|
+
@logger.info "Starting workflow in #{@registry[:run_dir]}"
|
228
|
+
begin
|
229
|
+
next_state
|
230
|
+
while @current_state != :finished && @current_state != :errored
|
231
|
+
# sleep 2
|
232
|
+
step
|
233
|
+
end
|
234
|
+
|
235
|
+
if !@options[:fast]
|
236
|
+
@logger.info 'Finished workflow - communicating results and zipping files'
|
237
|
+
@output_adapter.communicate_results(@registry[:run_dir], @registry[:results])
|
238
|
+
end
|
239
|
+
rescue StandardError => e
|
240
|
+
@logger.info "Error occurred during running with #{e.message}"
|
241
|
+
ensure
|
242
|
+
@logger.info 'Workflow complete'
|
243
|
+
|
244
|
+
# If we let the :reporting_measures step fail to continue with
|
245
|
+
# postprocess, we still want to report and error
|
246
|
+
if @final_message != ''
|
247
|
+
@current_state = :errored
|
248
|
+
end
|
249
|
+
|
250
|
+
if @current_state == :errored
|
251
|
+
@registry[:workflow_json].setCompletedStatus('Fail') if @registry[:workflow_json]
|
252
|
+
else
|
253
|
+
# completed status will already be set if workflow was halted
|
254
|
+
if @registry[:workflow_json].completedStatus.empty?
|
255
|
+
@registry[:workflow_json].setCompletedStatus('Success')
|
256
|
+
else
|
257
|
+
@current_state = :errored if @registry[:workflow_json].completedStatus.get == 'Fail'
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# save all files before calling output adapter
|
262
|
+
@registry[:log_targets].each(&:flush)
|
263
|
+
|
264
|
+
# save workflow with results
|
265
|
+
if @registry[:workflow_json] && !@options[:fast]
|
266
|
+
out_path = @registry[:workflow_json].absoluteOutPath
|
267
|
+
@registry[:workflow_json].saveAs(out_path)
|
268
|
+
end
|
269
|
+
|
270
|
+
# Write out the TimeLogger to the filesystem
|
271
|
+
@registry[:time_logger].save(File.join(@registry[:run_dir], 'profile.json')) if @registry[:time_logger]
|
272
|
+
|
273
|
+
if @current_state == :errored
|
274
|
+
@output_adapter.communicate_failure
|
275
|
+
else
|
276
|
+
@output_adapter.communicate_complete
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
@current_state
|
281
|
+
end
|
282
|
+
|
283
|
+
# Step through the states, if there is an error (e.g. exception) then go to error
|
284
|
+
#
|
285
|
+
def step
|
286
|
+
step_instance = @jobs.find { |h| h[:state] == @current_state }
|
287
|
+
require step_instance[:file]
|
288
|
+
klass = OpenStudio::Workflow.new_class(step_instance[:job], @input_adapter, @output_adapter, @registry, @options)
|
289
|
+
@output_adapter.communicate_transition("Starting state #{@current_state}", :state)
|
290
|
+
state_return = klass.perform
|
291
|
+
if state_return
|
292
|
+
@output_adapter.communicate_transition("Returned from state #{@current_state} with message #{state_return}", :state)
|
293
|
+
else
|
294
|
+
@output_adapter.communicate_transition("Returned from state #{@current_state}", :state)
|
295
|
+
end
|
296
|
+
next_state
|
297
|
+
rescue StandardError => e
|
298
|
+
step_error("#{e.message}:#{e.backtrace.join("\n")}")
|
299
|
+
end
|
300
|
+
|
301
|
+
# Error handling for when there is an exception running any of the state transitions
|
302
|
+
#
|
303
|
+
def step_error(*args)
|
304
|
+
# Make sure to set the instance variable @error to true in order to stop the :step
|
305
|
+
# event from being fired.
|
306
|
+
|
307
|
+
# Allow continuing anyways if it fails in reporting measures
|
308
|
+
if @current_state == :reporting_measures
|
309
|
+
@final_message = "Found error in state '#{@current_state}' with message #{args}}"
|
310
|
+
@logger.error @final_message
|
311
|
+
|
312
|
+
next_state
|
313
|
+
else
|
314
|
+
@final_message = "Found error in state '#{@current_state}' with message #{args}}"
|
315
|
+
@logger.error @final_message
|
316
|
+
|
317
|
+
# transition to an error state
|
318
|
+
@current_state = :errored
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# Return the finished state and exit
|
323
|
+
#
|
324
|
+
def run_finished(_, _, _)
|
325
|
+
logger.info "Running #{__method__}"
|
326
|
+
|
327
|
+
@current_state
|
328
|
+
end
|
329
|
+
|
330
|
+
private
|
331
|
+
|
332
|
+
# Advance the @current_state to the next state
|
333
|
+
#
|
334
|
+
def next_state
|
335
|
+
@logger.info "Current state: '#{@current_state}'"
|
336
|
+
ns = @jobs.find { |h| h[:state] == @current_state }[:next_state]
|
337
|
+
@logger.info "Next state will be: '#{ns}'"
|
338
|
+
@current_state = ns
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|