openstudio-workflow 0.0.1
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 +7 -0
- data/CHANGELOG.md +10 -0
- data/README.md +97 -0
- data/Rakefile +20 -0
- data/lib/openstudio/workflow/adapter.rb +68 -0
- data/lib/openstudio/workflow/adapters/local.rb +110 -0
- data/lib/openstudio/workflow/adapters/mongo.rb +259 -0
- data/lib/openstudio/workflow/jobs/run_energyplus/run_energyplus.rb +128 -0
- data/lib/openstudio/workflow/jobs/run_openstudio/monthly_report.idf +218 -0
- data/lib/openstudio/workflow/jobs/run_openstudio/run_openstudio.rb +344 -0
- data/lib/openstudio/workflow/jobs/run_postprocess/packaged_measures/README.md +5 -0
- data/lib/openstudio/workflow/jobs/run_postprocess/packaged_measures/StandardReports/measure.rb +212 -0
- data/lib/openstudio/workflow/jobs/run_postprocess/packaged_measures/StandardReports/measure.xml +53 -0
- data/lib/openstudio/workflow/jobs/run_postprocess/packaged_measures/StandardReports/resources/report.html.in +298 -0
- data/lib/openstudio/workflow/jobs/run_postprocess/run_postprocess.rb +549 -0
- data/lib/openstudio/workflow/jobs/run_preflight/run_preflight.rb +45 -0
- data/lib/openstudio/workflow/jobs/run_xml/run_xml.rb +279 -0
- data/lib/openstudio/workflow/multi_delegator.rb +48 -0
- data/lib/openstudio/workflow/run.rb +258 -0
- data/lib/openstudio/workflow/version.rb +24 -0
- data/lib/openstudio-workflow.rb +69 -0
- metadata +134 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cc2c789cb6f241ce8a9693f4fd52c2cea857a6ab
|
4
|
+
data.tar.gz: 8ead2192e95b45850c00a0b0e048d326eef05ead
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 31c526b09291097df08aef677a51e1dd46bfb64710c0a3a0fef1b6e2a8f9fc2ab0d23973afcdd651dedfe7f17f77266fcb6ef7d5df67a80d39addb590ef9b358
|
7
|
+
data.tar.gz: acaee1bf5ae39b1f4be3f67b4d292dfe790f0415e1f1aa04552bdae09ad1003ad0b44c45939a4b5f0f4ed76ff76ec1522871a61dd2fd5c508c8848b88685cbec
|
data/CHANGELOG.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# OpenStudio::Workflow
|
2
|
+
|
3
|
+
Run an EnergyPlus simulation using a file-based workflow that is read from a Local or MongoDB adapter.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
This applications has the following dependencies
|
8
|
+
|
9
|
+
* Ruby 2.0
|
10
|
+
* OpenStudio with Ruby 2.0 bindings
|
11
|
+
* EnergyPlus 8.1 (assuming OpenStudio ~> 1.3.1)
|
12
|
+
* MongoDB if using MongoDB Adapter (or when running rspec)
|
13
|
+
|
14
|
+
[OpenStudio](http://developer.nrel.gov/downloads/buildings/openstudio/builds/) needs to be installed
|
15
|
+
and in your path. On Mac/Linux it is easiest to add the following to your .bash_profile or /etc/profile.d in order
|
16
|
+
to make sure that OpenStudio can be loaded.
|
17
|
+
|
18
|
+
export OPENSTUDIO_ROOT=/usr/local
|
19
|
+
export RUBYLIB=$OPENSTUDIO_ROOT/lib/ruby/site_ruby/2.0.0
|
20
|
+
|
21
|
+
Add this line to your application's Gemfile:
|
22
|
+
|
23
|
+
gem 'OpenStudio-workflow'
|
24
|
+
|
25
|
+
Use this line if you want the bleeding edge:
|
26
|
+
|
27
|
+
gem 'OpenStudio-workflow', :git => 'git@github.com:NREL/OpenStudio-workflow-gem.git'
|
28
|
+
|
29
|
+
And then execute:
|
30
|
+
|
31
|
+
$ bundle
|
32
|
+
|
33
|
+
Or install it yourself as:
|
34
|
+
|
35
|
+
$ gem install OpenStudio-workflow
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
There are currently two adapters to run OpenStudio workflow. The first is a simple Local adapter
|
40
|
+
allowing the user to pass in the directory to simulation. The directory must have an
|
41
|
+
[analysis/problem JSON file](spec/files/local_ex1/analysis_1.json) and a [datapoint JSON file](spec/files/local_ex1/datapoint_1.json).
|
42
|
+
The workflow manager will use these data (and the measures, seed model, and weather data) to assemble and
|
43
|
+
execute the standard workflow of (preflight->openstudio measures->energyplus->postprocess).
|
44
|
+
|
45
|
+
r = OpenStudio::Workflow.load 'Local', '/home/user/a_directory', options
|
46
|
+
r.run
|
47
|
+
|
48
|
+
The workflow manager can also use MongoDB to receive instructions on the workflow to run and the data point values.
|
49
|
+
|
50
|
+
## Caveats and Todos
|
51
|
+
|
52
|
+
### Caveats
|
53
|
+
|
54
|
+
* There are currently several hard coded workflow options
|
55
|
+
* Must use OpenStudio with Ruby 2.0 support
|
56
|
+
* Using MongoDB as the Adapter requires a command line zip (gnuzip) utility
|
57
|
+
|
58
|
+
### Todos
|
59
|
+
|
60
|
+
* Read the analysis.json file to determine the states that are going to run instead of (or inaddition to) passing them into the constructor
|
61
|
+
* Implement better error handling with custom exception classes
|
62
|
+
* Implement a different measure directory, seed model directory, and weather file directory option
|
63
|
+
* Dynamically add other "states" to the workflow
|
64
|
+
* Create and change into a unique directory when running measures
|
65
|
+
* ~~Implement Error State~~
|
66
|
+
* ~~Implement MongoDB Adapter~~
|
67
|
+
* ~~Implement remaining Adapter states (i.e. communicate success, communicate failure etc~~
|
68
|
+
* Add a results adapter to return a string as the last call based on the source of the call. (e.g. R, command line, C++, etc).
|
69
|
+
* Implement a logger in the Adapters, right now they are unable to log
|
70
|
+
* Hook up the measure groups based workflows
|
71
|
+
* More tests!
|
72
|
+
* ~~Add xml workflow item~~
|
73
|
+
|
74
|
+
## Testing and Development
|
75
|
+
|
76
|
+
Depending on what adapter is being tested it may be preferable to skip installing various gems. This can be done by calling
|
77
|
+
|
78
|
+
bundle install --without mongo
|
79
|
+
|
80
|
+
On Windows it is recommended to bundle without mongo nor ci as they may require native extensions.
|
81
|
+
|
82
|
+
bundle install --without mongo ci
|
83
|
+
|
84
|
+
### Testing
|
85
|
+
|
86
|
+
Run `rspec` or `rake` to execute the tests.
|
87
|
+
|
88
|
+
## Contributing
|
89
|
+
|
90
|
+
1. Fork it ( https://github.com/NREL/OpenStudio-workflow/fork )
|
91
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
92
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
93
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
94
|
+
5. Create a new Pull Request
|
95
|
+
|
96
|
+
## Development
|
97
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
begin
|
4
|
+
Bundler.setup
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts 'Run `bundle install` to install missing gems'
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'rake'
|
12
|
+
require 'rspec/core'
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
|
15
|
+
#require 'rubocop/rake_task'
|
16
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
17
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
18
|
+
end
|
19
|
+
|
20
|
+
task default: [:spec]
|
@@ -0,0 +1,68 @@
|
|
1
|
+
######################################################################
|
2
|
+
# Copyright (c) 2008-2014, Alliance for Sustainable Energy.
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
+
######################################################################
|
19
|
+
|
20
|
+
# Adapter class to decide where to obtain instructions to run the simulation workflow
|
21
|
+
module OpenStudio
|
22
|
+
module Workflow
|
23
|
+
class Adapter
|
24
|
+
|
25
|
+
attr_accessor :options
|
26
|
+
|
27
|
+
def initialize(options={})
|
28
|
+
@options = options
|
29
|
+
@log = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
#class << self
|
33
|
+
#attr_reader :problem
|
34
|
+
|
35
|
+
def load(filename, options={})
|
36
|
+
instance.load(filename, options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def communicate_started(id, options = {})
|
40
|
+
instance.communicate_started id
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_datapoint(id, options={})
|
44
|
+
instance.get_datapoint id, options
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_problem(id, options = {})
|
48
|
+
instance.get_problem id, options
|
49
|
+
end
|
50
|
+
|
51
|
+
def communicate_results(id, results)
|
52
|
+
instance.communicate_results id, results
|
53
|
+
end
|
54
|
+
|
55
|
+
def communicate_complete(id)
|
56
|
+
instance.communicate_complete id
|
57
|
+
end
|
58
|
+
|
59
|
+
def communicate_failure(id)
|
60
|
+
instance.communicate_failure id
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_logger(file, options={})
|
64
|
+
instance.get_logger file, options
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
######################################################################
|
2
|
+
# Copyright (c) 2008-2014, Alliance for Sustainable Energy.
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
+
######################################################################
|
19
|
+
|
20
|
+
require_relative '../adapter'
|
21
|
+
|
22
|
+
# Local file based workflow
|
23
|
+
module OpenStudio
|
24
|
+
module Workflow
|
25
|
+
module Adapters
|
26
|
+
class Local < Adapter
|
27
|
+
def initialize(options={})
|
28
|
+
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
# Tell the system that the process has started
|
33
|
+
def communicate_started(directory, options = {})
|
34
|
+
# Watch out for namespace conflicts (::Time is okay but Time is OpenStudio::Time)
|
35
|
+
File.open("#{directory}/started.job", 'w') { |f| f << "Started Workflow #{::Time.now}" }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Get the data point from the path
|
39
|
+
def get_datapoint(directory, options={})
|
40
|
+
defaults = {datapoint_filename: 'datapoint.json', format: 'json'}
|
41
|
+
options = defaults.merge(options)
|
42
|
+
|
43
|
+
# how do we log within this file?
|
44
|
+
if File.exist? "#{directory}/#{options[:datapoint_filename]}"
|
45
|
+
::MultiJson.load(File.read("#{directory}/#{options[:datapoint_filename]}"), symbolize_names: true)
|
46
|
+
else
|
47
|
+
fail "Data point file does not exist for #{directory}/#{options[:datapoint_filename]}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get the Problem/Analysis definition from the local file
|
52
|
+
# TODO: rename this to get_analysis_definintion (or something like that)
|
53
|
+
def get_problem(directory, options = {})
|
54
|
+
defaults = {problem_filename: 'problem.json', format: 'json'}
|
55
|
+
options = defaults.merge(options)
|
56
|
+
|
57
|
+
if File.exist? "#{directory}/#{options[:problem_filename]}"
|
58
|
+
::MultiJson.load(File.read("#{directory}/#{options[:problem_filename]}"), symbolize_names: true)
|
59
|
+
else
|
60
|
+
fail "Problem file does not exist for #{directory}/#{options[:problem_filename]}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def communicate_intermediate_result(directory)
|
65
|
+
# noop
|
66
|
+
end
|
67
|
+
|
68
|
+
def communicate_complete(directory)
|
69
|
+
File.open("#{directory}/finished.job", 'w') { |f| f << "Finished Workflow #{::Time.now}" }
|
70
|
+
end
|
71
|
+
|
72
|
+
# Final state of the simulation. The os_directory is the run directory and may be needed to
|
73
|
+
# zip up the results of the simuation.
|
74
|
+
def communicate_failure(directory)
|
75
|
+
File.open("#{directory}/failed.job", 'w') { |f| f << "Failed Workflow #{::Time.now}" }
|
76
|
+
#@communicate_module.communicate_failure(@communicate_object, os_directory)
|
77
|
+
end
|
78
|
+
|
79
|
+
def communicate_results(directory, results)
|
80
|
+
if results.is_a? Hash
|
81
|
+
File.open("#{directory}/datapoint_out.json", 'w') { |f| f << JSON.pretty_generate(results) }
|
82
|
+
else
|
83
|
+
pp "Unknown datapoint result type. Please handle #{results.class}"
|
84
|
+
#data_point_json_path = OpenStudio::Path.new(run_dir) / OpenStudio::Path.new('data_point_out.json')
|
85
|
+
#os_data_point.saveJSON(data_point_json_path, true)
|
86
|
+
end
|
87
|
+
#end
|
88
|
+
end
|
89
|
+
|
90
|
+
# TODO: can this be deprecated in favor a checking the class?
|
91
|
+
def communicate_results_json(eplus_json, analysis_dir)
|
92
|
+
# noop
|
93
|
+
end
|
94
|
+
|
95
|
+
def reload
|
96
|
+
# noop
|
97
|
+
end
|
98
|
+
|
99
|
+
# For the local adapter send back a handle to a file to append the data. For this adapter
|
100
|
+
# the log messages are likely to be the same as the run.log messages.
|
101
|
+
# TODO: do we really want two local logs from the Local adapter? One is in the run dir and the other is in the root
|
102
|
+
def get_logger(directory, options={})
|
103
|
+
@log ||= File.open("#{directory}/local_adapter.log", "w")
|
104
|
+
@log
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
######################################################################
|
2
|
+
# Copyright (c) 2008-2014, Alliance for Sustainable Energy.
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
+
######################################################################
|
19
|
+
|
20
|
+
require_relative '../adapter'
|
21
|
+
|
22
|
+
module OpenStudio
|
23
|
+
module Workflow
|
24
|
+
module Adapters
|
25
|
+
class MongoLog
|
26
|
+
def initialize(datapoint_model)
|
27
|
+
@dp = datapoint_model
|
28
|
+
@dp.sdp_log_file ||= []
|
29
|
+
end
|
30
|
+
|
31
|
+
def write(msg)
|
32
|
+
@dp.sdp_log_file << msg.gsub('\n', '')
|
33
|
+
@dp.save!
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Mongo < Adapter
|
38
|
+
attr_reader :datapoint
|
39
|
+
|
40
|
+
def initialize(options={})
|
41
|
+
super
|
42
|
+
|
43
|
+
require 'mongoid'
|
44
|
+
require 'mongoid_paperclip'
|
45
|
+
require 'delayed_job_mongoid'
|
46
|
+
base_path = @options[:mongoid_path] ? @options[:mongoid_path] : "#{File.dirname(__FILE__)}/mongo"
|
47
|
+
|
48
|
+
Dir["#{base_path}/models/*.rb"].each { |f| require f }
|
49
|
+
Mongoid.load!("#{base_path}/mongoid.yml", :development)
|
50
|
+
|
51
|
+
@datapoint = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Tell the system that the process has started
|
55
|
+
def communicate_started(directory, options={})
|
56
|
+
# Watch out for namespace conflicts (::Time is okay but Time is OpenStudio::Time)
|
57
|
+
File.open("#{directory}/started.job", 'w') { |f| f << "Started Workflow #{::Time.now}" }
|
58
|
+
|
59
|
+
@datapoint ||= get_datapoint_model(options[:datapoint_id])
|
60
|
+
@datapoint.status = 'started'
|
61
|
+
@datapoint.status_message = ''
|
62
|
+
@datapoint.run_start_time = ::Time.now
|
63
|
+
|
64
|
+
|
65
|
+
# TODO: use a different method to determine if this is an amazon account
|
66
|
+
# TODO: use the ComputeNode model to pull out the information so that we can reuse the methods
|
67
|
+
# Determine what the IP address is of the worker node and save in the data point
|
68
|
+
require 'socket'
|
69
|
+
if Socket.gethostname =~ /os-.*/
|
70
|
+
# Maybe use this in the future: /sbin/ifconfig eth1|grep inet|head -1|sed 's/\:/ /'|awk '{print $3}'
|
71
|
+
# Must be on vagrant and just use the hostname to do a lookup
|
72
|
+
map = {
|
73
|
+
'os-server' => '192.168.33.10',
|
74
|
+
'os-worker-1' => '192.168.33.11',
|
75
|
+
'os-worker-2' => '192.168.33.12'
|
76
|
+
}
|
77
|
+
@datapoint.ip_address = map[Socket.gethostname]
|
78
|
+
@datapoint.internal_ip_address = @datapoint.ip_address
|
79
|
+
|
80
|
+
# TODO: add back in the instance id
|
81
|
+
elsif Socket.gethostname =~ /instance-data.ec2.internal.*/i
|
82
|
+
# On amazon, you have to hit an API to determine the IP address because
|
83
|
+
# of the internal/external ip addresses
|
84
|
+
|
85
|
+
# NL: add the suppress
|
86
|
+
public_ip_address = `curl -sL http://169.254.169.254/latest/meta-data/public-ipv4`
|
87
|
+
internal_ip_address = `curl -sL http://169.254.169.254/latest/meta-data/local-ipv4`
|
88
|
+
# instance_information = `curl -sL http://169.254.169.254/latest/meta-data/instance-id`
|
89
|
+
# instance_information = `curl -sL http://169.254.169.254/latest/meta-data/ami-id`
|
90
|
+
@datapoint.ip_address = public_ip_address
|
91
|
+
@datapoint.internal_ip_address = internal_ip_address
|
92
|
+
# @datapoint.server_information = instance_information
|
93
|
+
else
|
94
|
+
if Gem.loaded_specs["facter"]
|
95
|
+
# TODO: add hostname via the facter gem (and anything else?)
|
96
|
+
@datapoint.ip_address = Facter.fact(:ipaddress).value
|
97
|
+
@datapoint.internal_ip_address = Facter.fact(:hostname).value
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
@datapoint.save!
|
102
|
+
end
|
103
|
+
|
104
|
+
# Get the data point from the path
|
105
|
+
def get_datapoint(directory, options={})
|
106
|
+
# TODO : make this a conditional on when to create one vs when to error out.
|
107
|
+
# keep @datapoint as the model instance
|
108
|
+
@datapoint = DataPoint.find_or_create_by(uuid: options[:datapoint_id])
|
109
|
+
|
110
|
+
# convert to JSON for the workflow - and rearrange the version (fix THIS)
|
111
|
+
datapoint_hash = {}
|
112
|
+
unless @datapoint.nil?
|
113
|
+
datapoint_hash[:data_point] = @datapoint.as_document.to_hash
|
114
|
+
# TODO: Can i remove this openstudio_version stuff?
|
115
|
+
#datapoint_hash[:openstudio_version] = datapoint_hash[:openstudio_version]
|
116
|
+
|
117
|
+
# TODO: need to figure out how to get symbols from mongo.
|
118
|
+
datapoint_hash = MultiJson.load(MultiJson.dump(datapoint_hash, pretty: true), symbolize_keys: true)
|
119
|
+
else
|
120
|
+
fail "Could not find datapoint"
|
121
|
+
end
|
122
|
+
|
123
|
+
datapoint_hash
|
124
|
+
end
|
125
|
+
|
126
|
+
# TODO: cleanup these options. Make them part of the class. They are just unwieldly here.
|
127
|
+
def get_problem(directory, options = {})
|
128
|
+
defaults = {format: 'json'}
|
129
|
+
options = defaults.merge(options)
|
130
|
+
|
131
|
+
get_datapoint(directory, options) unless @datapoint
|
132
|
+
|
133
|
+
if @datapoint
|
134
|
+
analysis = @datapoint.analysis.as_document.to_hash
|
135
|
+
else
|
136
|
+
fail "Cannot retrieve problem because datapoint was nil"
|
137
|
+
end
|
138
|
+
|
139
|
+
analysis_hash = {}
|
140
|
+
if analysis
|
141
|
+
analysis_hash[:analysis] = analysis
|
142
|
+
analysis_hash[:openstudio_version] = analysis[:openstudio_version]
|
143
|
+
|
144
|
+
# TODO: need to figure out how to get symbols from mongo.
|
145
|
+
analysis_hash = MultiJson.load(MultiJson.dump(analysis_hash, pretty: true), symbolize_keys: true)
|
146
|
+
end
|
147
|
+
analysis_hash
|
148
|
+
end
|
149
|
+
|
150
|
+
def communicate_intermediate_result(directory)
|
151
|
+
# noop
|
152
|
+
end
|
153
|
+
|
154
|
+
def communicate_complete(directory)
|
155
|
+
@datapoint.run_end_time = ::Time.now
|
156
|
+
@datapoint.status = 'completed'
|
157
|
+
@datapoint.status_message = 'completed normal'
|
158
|
+
@datapoint.save!
|
159
|
+
end
|
160
|
+
|
161
|
+
# Final state of the simulation. The os_directory is the run directory and may be needed to
|
162
|
+
# zip up the results of the simuation.
|
163
|
+
def communicate_failure(directory)
|
164
|
+
# zip up the folder even on datapoint failures
|
165
|
+
if directory && File.exist?(directory)
|
166
|
+
zip_results(directory)
|
167
|
+
end
|
168
|
+
|
169
|
+
@datapoint.run_end_time = ::Time.now
|
170
|
+
@datapoint.status = 'completed'
|
171
|
+
@datapoint.status_message = 'datapoint failure'
|
172
|
+
@datapoint.save!
|
173
|
+
end
|
174
|
+
|
175
|
+
def communicate_results(directory, results)
|
176
|
+
zip_results(directory, 'workflow')
|
177
|
+
|
178
|
+
#@logger.info 'Saving EnergyPlus JSON file'
|
179
|
+
if results
|
180
|
+
@datapoint.results ? @datapoint.results.merge!(eplus_json) : @datapoint.results = results
|
181
|
+
end
|
182
|
+
result = @datapoint.save! # redundant because next method calls save too.
|
183
|
+
if result
|
184
|
+
#@logger.info 'Successfully saved result to database'
|
185
|
+
else
|
186
|
+
#@logger.error 'ERROR saving result to database'
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# TODO: can this be deprecated in favor a checking the class?
|
191
|
+
def communicate_results_json(eplus_json, analysis_dir)
|
192
|
+
# noop
|
193
|
+
end
|
194
|
+
|
195
|
+
# TODO: not needed anymore i think...
|
196
|
+
def reload
|
197
|
+
# noop
|
198
|
+
end
|
199
|
+
|
200
|
+
# TODO: Implement the writing to the mongo_db for logging
|
201
|
+
def get_logger(directory, options={})
|
202
|
+
# get the datapoint object
|
203
|
+
get_datapoint(directory, options) unless @datapoint
|
204
|
+
@log = OpenStudio::Workflow::Adapters::MongoLog.new(@datapoint)
|
205
|
+
@log
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
def get_datapoint_model(uuid)
|
211
|
+
# TODO : make this a conditional on when to create one vs when to error out.
|
212
|
+
# keep @datapoint as the model instance
|
213
|
+
DataPoint.find_or_create_by(uuid: uuid)
|
214
|
+
end
|
215
|
+
|
216
|
+
# TODO: this uses a system call to zip results at the moment
|
217
|
+
def zip_results(analysis_dir, analysis_type = 'workflow')
|
218
|
+
eplus_search_path = nil
|
219
|
+
current_dir = Dir.pwd
|
220
|
+
FileUtils.mkdir_p "#{analysis_dir}/reports"
|
221
|
+
case analysis_type
|
222
|
+
when 'workflow'
|
223
|
+
eplus_search_path = "#{analysis_dir}/*run*/eplustbl.htm"
|
224
|
+
when 'runmanager'
|
225
|
+
eplus_search_path = "#{analysis_dir}/*EnergyPlus*/eplustbl.htm"
|
226
|
+
end
|
227
|
+
|
228
|
+
# copy some files into a report folder
|
229
|
+
eplus_html = Dir.glob(eplus_search_path).last || nil
|
230
|
+
if eplus_html
|
231
|
+
#@logger.info "Checking for HTML Report: #{eplus_html}"
|
232
|
+
if File.exist? eplus_html
|
233
|
+
# do some encoding on the html if possible
|
234
|
+
html = File.read(eplus_html)
|
235
|
+
html = html.force_encoding('ISO-8859-1').encode('utf-8', replace: nil)
|
236
|
+
File.open("#{analysis_dir}/reports/eplustbl.html", 'w') { |f| f << html }
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# create zip file using a system call
|
241
|
+
#@logger.info "Zipping up Analysis Directory #{analysis_dir}"
|
242
|
+
if File.directory? analysis_dir
|
243
|
+
Dir.chdir(analysis_dir)
|
244
|
+
`zip -9 -r --exclude=*.rb* data_point_#{@datapoint.uuid}.zip .`
|
245
|
+
end
|
246
|
+
|
247
|
+
# zip up only the reports folder
|
248
|
+
report_dir = "#{analysis_dir}"
|
249
|
+
#@logger.info "Zipping up Analysis Reports Directory #{report_dir}/reports"
|
250
|
+
if File.directory? report_dir
|
251
|
+
Dir.chdir(report_dir)
|
252
|
+
`zip -r data_point_#{@datapoint.uuid}_reports.zip reports`
|
253
|
+
end
|
254
|
+
Dir.chdir(current_dir)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|