urbanopt-scenario 0.2.0.pre2 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +5 -5
  2. data/.github/pull_request_template.md +2 -2
  3. data/.gitignore +2 -0
  4. data/.rubocop.yml +1 -1
  5. data/CHANGELOG.md +82 -1
  6. data/CONTRIBUTING.md +1 -1
  7. data/Gemfile +28 -18
  8. data/Jenkinsfile +1 -1
  9. data/LICENSE.md +1 -1
  10. data/RDOC_MAIN.md +1 -1
  11. data/README.md +1 -1
  12. data/Rakefile +2 -2
  13. data/docs/README.md +1 -1
  14. data/docs/package-lock.json +2499 -2322
  15. data/docs/package.json +13 -9
  16. data/lib/urbanopt-scenario.rb +1 -1
  17. data/lib/urbanopt/scenario.rb +3 -1
  18. data/lib/urbanopt/scenario/default_reports.rb +3 -8
  19. data/lib/urbanopt/scenario/extension.rb +1 -1
  20. data/lib/urbanopt/scenario/logger.rb +1 -1
  21. data/lib/urbanopt/scenario/scenario_base.rb +1 -1
  22. data/lib/urbanopt/scenario/scenario_csv.rb +22 -9
  23. data/lib/urbanopt/scenario/scenario_datapoint_base.rb +8 -1
  24. data/lib/urbanopt/scenario/scenario_post_processor_base.rb +1 -1
  25. data/lib/urbanopt/scenario/scenario_post_processor_default.rb +99 -8
  26. data/lib/urbanopt/scenario/scenario_post_processor_opendss.rb +275 -0
  27. data/lib/urbanopt/scenario/scenario_runner_base.rb +2 -2
  28. data/lib/urbanopt/scenario/scenario_runner_osw.rb +23 -9
  29. data/lib/urbanopt/scenario/scenario_visualization.rb +236 -0
  30. data/lib/urbanopt/scenario/simulation_dir_base.rb +1 -1
  31. data/lib/urbanopt/scenario/simulation_dir_osw.rb +2 -9
  32. data/lib/urbanopt/scenario/simulation_mapper_base.rb +1 -1
  33. data/lib/urbanopt/scenario/version.rb +2 -2
  34. data/package-lock.json +3 -0
  35. data/urbanopt-scenario-gem.gemspec +15 -15
  36. metadata +78 -82
  37. data/doc_templates/LICENSE.md +0 -27
  38. data/doc_templates/README.md.erb +0 -42
  39. data/doc_templates/copyright_erb.txt +0 -31
  40. data/doc_templates/copyright_js.txt +0 -4
  41. data/doc_templates/copyright_ruby.txt +0 -29
  42. data/lib/change_log.rb +0 -147
  43. data/lib/measures/.rubocop.yml +0 -5
  44. data/lib/measures/default_feature_reports/LICENSE.md +0 -27
  45. data/lib/measures/default_feature_reports/README.md +0 -56
  46. data/lib/measures/default_feature_reports/README.md.erb +0 -42
  47. data/lib/measures/default_feature_reports/measure.rb +0 -957
  48. data/lib/measures/default_feature_reports/measure.xml +0 -143
  49. data/lib/measures/default_feature_reports/tests/USA_CO_Golden-NREL.724666_TMY3.epw +0 -8768
  50. data/lib/measures/default_feature_reports/tests/default_feature_reports_test.rb +0 -238
  51. data/lib/measures/default_feature_reports/tests/example_model.osm +0 -4378
  52. data/lib/urbanopt/scenario/default_reports/construction_cost.rb +0 -169
  53. data/lib/urbanopt/scenario/default_reports/date.rb +0 -97
  54. data/lib/urbanopt/scenario/default_reports/distributed_generation.rb +0 -374
  55. data/lib/urbanopt/scenario/default_reports/end_use.rb +0 -159
  56. data/lib/urbanopt/scenario/default_reports/end_uses.rb +0 -140
  57. data/lib/urbanopt/scenario/default_reports/feature_report.rb +0 -260
  58. data/lib/urbanopt/scenario/default_reports/generator.rb +0 -92
  59. data/lib/urbanopt/scenario/default_reports/location.rb +0 -99
  60. data/lib/urbanopt/scenario/default_reports/logger.rb +0 -44
  61. data/lib/urbanopt/scenario/default_reports/program.rb +0 -266
  62. data/lib/urbanopt/scenario/default_reports/reporting_period.rb +0 -301
  63. data/lib/urbanopt/scenario/default_reports/scenario_report.rb +0 -311
  64. data/lib/urbanopt/scenario/default_reports/schema/README.md +0 -33
  65. data/lib/urbanopt/scenario/default_reports/schema/scenario_csv_columns.txt +0 -21
  66. data/lib/urbanopt/scenario/default_reports/schema/scenario_schema.json +0 -839
  67. data/lib/urbanopt/scenario/default_reports/solar_pv.rb +0 -93
  68. data/lib/urbanopt/scenario/default_reports/storage.rb +0 -105
  69. data/lib/urbanopt/scenario/default_reports/timeseries_csv.rb +0 -284
  70. data/lib/urbanopt/scenario/default_reports/validator.rb +0 -97
  71. data/lib/urbanopt/scenario/default_reports/wind.rb +0 -92
@@ -1,27 +0,0 @@
1
- URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
- contributors. All rights reserved.
3
-
4
- Redistribution and use in source and binary forms, with or without modification,
5
- are permitted provided that the following conditions are met:
6
-
7
- Redistributions of source code must retain the above copyright notice, this list
8
- of conditions and the following disclaimer.
9
-
10
- Redistributions in binary form must reproduce the above copyright notice, this
11
- list of conditions and the following disclaimer in the documentation and/or other
12
- materials provided with the distribution.
13
-
14
- Neither the name of the copyright holder nor the names of its contributors may be
15
- used to endorse or promote products derived from this software without specific
16
- prior written permission.
17
-
18
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
- IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23
- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26
- OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27
- OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -1,42 +0,0 @@
1
- <%#= README.md.erb is used to auto-generate README.md. %>
2
- <%#= To manually maintain README.md throw away README.md.erb and manually edit README.md %>
3
- ###### (Automatically generated documentation)
4
-
5
- # <%= name %>
6
-
7
- ## Description
8
- <%= description %>
9
-
10
- ## Modeler Description
11
- <%= modelerDescription %>
12
-
13
- ## Measure Type
14
- <%= measureType %>
15
-
16
- ## Taxonomy
17
- <%= taxonomy %>
18
-
19
- ## Arguments
20
-
21
- <% arguments.each do |argument| %>
22
- ### <%= argument[:display_name] %>
23
- <%= argument[:description] %>
24
- **Name:** <%= argument[:name] %>,
25
- **Type:** <%= argument[:type] %>,
26
- **Units:** <%= argument[:units] %>,
27
- **Required:** <%= argument[:required] %>,
28
- **Model Dependent:** <%= argument[:model_dependent] %>
29
- <% end %>
30
-
31
- <% if arguments.size == 0 %>
32
- <%= "This measure does not have any user arguments" %>
33
- <% end %>
34
-
35
- <% if outputs.size > 0 %>
36
- ## Outputs
37
- <% output_names = [] %>
38
- <% outputs.each do |output| %>
39
- <% output_names << output[:display_name] %>
40
- <% end %>
41
- <%= output_names.join(", ") %>
42
- <% end %>
@@ -1,31 +0,0 @@
1
- <%
2
- # *********************************************************************************
3
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
4
- # contributors. All rights reserved.
5
- #
6
- # Redistribution and use in source and binary forms, with or without modification,
7
- # are permitted provided that the following conditions are met:
8
- #
9
- # Redistributions of source code must retain the above copyright notice, this list
10
- # of conditions and the following disclaimer.
11
- #
12
- # Redistributions in binary form must reproduce the above copyright notice, this
13
- # list of conditions and the following disclaimer in the documentation and/or other
14
- # materials provided with the distribution.
15
- #
16
- # Neither the name of the copyright holder nor the names of its contributors may be
17
- # used to endorse or promote products derived from this software without specific
18
- # prior written permission.
19
- #
20
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22
- # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23
- # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24
- # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25
- # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
- # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
- # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
28
- # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29
- # OF THE POSSIBILITY OF SUCH DAMAGE.
30
- # *********************************************************************************
31
- %>
@@ -1,4 +0,0 @@
1
- /* @preserve
2
- * URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved.
3
- * Use of this source code is governed by the BSD 3-Clause license.
4
- */
@@ -1,29 +0,0 @@
1
- # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
- # contributors. All rights reserved.
4
- #
5
- # Redistribution and use in source and binary forms, with or without modification,
6
- # are permitted provided that the following conditions are met:
7
- #
8
- # Redistributions of source code must retain the above copyright notice, this list
9
- # of conditions and the following disclaimer.
10
- #
11
- # Redistributions in binary form must reproduce the above copyright notice, this
12
- # list of conditions and the following disclaimer in the documentation and/or other
13
- # materials provided with the distribution.
14
- #
15
- # Neither the name of the copyright holder nor the names of its contributors may be
16
- # used to endorse or promote products derived from this software without specific
17
- # prior written permission.
18
- #
19
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
- # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
- # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23
- # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24
- # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
- # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
- # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27
- # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28
- # OF THE POSSIBILITY OF SUCH DAMAGE.
29
- # *********************************************************************************
@@ -1,147 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'github_api'
4
- require 'date'
5
- require 'optparse'
6
- require 'optparse/date'
7
-
8
- # Instructions:
9
- # Get a token from github's settings (https://github.com/settings/tokens)
10
- #
11
- # Example:
12
- # ruby change_log.rb -t abcdefghijklmnopqrstuvwxyz -s 2017-09-06
13
- #
14
-
15
- options = {}
16
- OptionParser.new do |opts|
17
- opts.banner = "Usage: change_log.rb [options]\n" \
18
- 'Prints New, Open, Closed Issues, and number of accepted PRs'
19
- opts.separator ''
20
-
21
- # defaults, go back 90 days
22
- options[:start_date] = Date.today - 90
23
- options[:end_date] = Date.today
24
-
25
- opts.on('-s', '--start-date [DATE]', Date, 'Start of data (e.g. 2017-09-06)') do |v|
26
- options[:start_date] = v
27
- end
28
- opts.on('-e', '--end-date [DATE]', Date, 'End of data (e.g. 2017-09-13)') do |v|
29
- options[:end_date] = v
30
- end
31
- opts.on('-t', '--token [String]', String, 'Github API Token') do |v|
32
- options[:token] = v
33
- end
34
- end.parse!
35
-
36
- # Convert dates to time objects
37
- options[:start_date] = Time.parse(options[:start_date].to_s)
38
- options[:end_date] = Time.parse(options[:end_date].to_s)
39
- puts options
40
-
41
- ### Repository options
42
- repo_owner = 'URBANopt'
43
- repo = 'urbanopt-scenario-gem'
44
-
45
- github = Github.new
46
- if options[:token]
47
- puts 'Using github token'
48
- github = Github.new oauth_token: options[:token]
49
- end
50
-
51
- total_open_issues = []
52
- total_open_pull_requests = []
53
- new_issues = []
54
- closed_issues = []
55
- accepted_pull_requests = []
56
-
57
- def get_num(issue)
58
- issue.html_url.split('/')[-1].to_i
59
- end
60
-
61
- def get_issue_num(issue)
62
- "\##{get_num(issue)}"
63
- end
64
-
65
- def get_html_url(issue)
66
- issue.html_url
67
- end
68
-
69
- def get_title(issue)
70
- issue.title
71
- end
72
-
73
- def print_issue(issue)
74
- is_feature = false
75
- issue.labels.each { |label| is_feature = true if label.name == 'Feature Request' }
76
-
77
- if is_feature
78
- "- Improved [#{get_issue_num(issue)}]( #{get_html_url(issue)} ), #{get_title(issue)}"
79
- else
80
- "- Fixed [#{get_issue_num(issue)}]( #{get_html_url(issue)} ), #{get_title(issue)}"
81
- end
82
- end
83
-
84
- # Process Open Issues
85
- results = -1
86
- page = 1
87
- while results != 0
88
- resp = github.issues.list user: repo_owner, repo: repo, sort: 'created', direction: 'asc',
89
- state: 'open', per_page: 100, page: page
90
- results = resp.length
91
- resp.env[:body].each do |issue, _index|
92
- created = Time.parse(issue.created_at)
93
- if !issue.key?(:pull_request)
94
- total_open_issues << issue
95
- if created >= options[:start_date] && created <= options[:end_date]
96
- new_issues << issue
97
- end
98
- else
99
- total_open_pull_requests << issue
100
- end
101
- end
102
-
103
- page += 1
104
- end
105
-
106
- # Process Closed Issues
107
- results = -1
108
- page = 1
109
- while results != 0
110
- resp = github.issues.list user: repo_owner, repo: repo, sort: 'created', direction: 'asc',
111
- state: 'closed', per_page: 100, page: page
112
- results = resp.length
113
- resp.env[:body].each do |issue, _index|
114
- created = Time.parse(issue.created_at)
115
- closed = Time.parse(issue.closed_at)
116
- if !issue.key?(:pull_request)
117
- if created >= options[:start_date] && created <= options[:end_date]
118
- new_issues << issue
119
- end
120
- if closed >= options[:start_date] && closed <= options[:end_date]
121
- closed_issues << issue
122
- end
123
- elsif closed >= options[:start_date] && closed <= options[:end_date]
124
- accepted_pull_requests << issue
125
- end
126
- end
127
-
128
- page += 1
129
- end
130
-
131
- closed_issues.sort! { |x, y| get_num(x) <=> get_num(y) }
132
- new_issues.sort! { |x, y| get_num(x) <=> get_num(y) }
133
- accepted_pull_requests.sort! { |x, y| get_num(x) <=> get_num(y) }
134
- total_open_pull_requests.sort! { |x, y| get_num(x) <=> get_num(y) }
135
-
136
- puts "Total Open Issues: #{total_open_issues.length}"
137
- puts "Total Open Pull Requests: #{total_open_pull_requests.length}"
138
- puts "\nDate Range: #{options[:start_date].strftime('%m/%d/%y')} - #{options[:end_date].strftime('%m/%d/%y')}:"
139
- puts "\nNew Issues: #{new_issues.length} (" + new_issues.map { |issue| get_issue_num(issue) }.join(', ') + ')'
140
-
141
- puts "\nClosed Issues: #{closed_issues.length}"
142
- closed_issues.each { |issue| puts print_issue(issue) }
143
-
144
- puts "\nAccepted Pull Requests: #{accepted_pull_requests.length}"
145
- accepted_pull_requests.each { |issue| puts print_issue(issue) }
146
-
147
- puts "\nAll Open Issues: #{total_open_issues.length} (" + total_open_issues.map { |issue| get_issue_num(issue) }.join(', ') + ')'
@@ -1,5 +0,0 @@
1
- AllCops:
2
- Exclude:
3
- - 'spec/test_measures/**/*'
4
- inherit_from:
5
- - http://s3.amazonaws.com/openstudio-resources/styles/rubocop.yml
@@ -1,27 +0,0 @@
1
- URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
- contributors. All rights reserved.
3
-
4
- Redistribution and use in source and binary forms, with or without modification,
5
- are permitted provided that the following conditions are met:
6
-
7
- Redistributions of source code must retain the above copyright notice, this list
8
- of conditions and the following disclaimer.
9
-
10
- Redistributions in binary form must reproduce the above copyright notice, this
11
- list of conditions and the following disclaimer in the documentation and/or other
12
- materials provided with the distribution.
13
-
14
- Neither the name of the copyright holder nor the names of its contributors may be
15
- used to endorse or promote products derived from this software without specific
16
- prior written permission.
17
-
18
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
- IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22
- INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23
- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26
- OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27
- OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -1,56 +0,0 @@
1
-
2
-
3
- ###### (Automatically generated documentation)
4
-
5
- # DefaultFeatureReports
6
-
7
- ## Description
8
- Writes default_feature_reports.json and default_feature_reports.csv files used by URBANopt Scenario Default Post Processor
9
-
10
- ## Modeler Description
11
- This measure only allows for one feature_report per simulation. If multiple features are simulated in a single simulation, a new measure must be written to disaggregate simulation results to multiple features.
12
-
13
- ## Measure Type
14
- ReportingMeasure
15
-
16
- ## Taxonomy
17
-
18
-
19
- ## Arguments
20
-
21
-
22
- ### Feature unique identifier
23
-
24
- **Name:** feature_id,
25
- **Type:** String,
26
- **Units:** ,
27
- **Required:** false,
28
- **Model Dependent:** false
29
-
30
- ### Feature scenario specific name
31
-
32
- **Name:** feature_name,
33
- **Type:** String,
34
- **Units:** ,
35
- **Required:** false,
36
- **Model Dependent:** false
37
-
38
- ### URBANopt Feature Type
39
-
40
- **Name:** feature_type,
41
- **Type:** String,
42
- **Units:** ,
43
- **Required:** false,
44
- **Model Dependent:** false
45
-
46
- ### Reporting Frequency
47
- The frequency at which to report timeseries output data.
48
- **Name:** reporting_frequency,
49
- **Type:** Choice,
50
- **Units:** ,
51
- **Required:** true,
52
- **Model Dependent:** false
53
-
54
-
55
-
56
-
@@ -1,42 +0,0 @@
1
- <%#= README.md.erb is used to auto-generate README.md. %>
2
- <%#= To manually maintain README.md throw away README.md.erb and manually edit README.md %>
3
- ###### (Automatically generated documentation)
4
-
5
- # <%= name %>
6
-
7
- ## Description
8
- <%= description %>
9
-
10
- ## Modeler Description
11
- <%= modelerDescription %>
12
-
13
- ## Measure Type
14
- <%= measureType %>
15
-
16
- ## Taxonomy
17
- <%= taxonomy %>
18
-
19
- ## Arguments
20
-
21
- <% arguments.each do |argument| %>
22
- ### <%= argument[:display_name] %>
23
- <%= argument[:description] %>
24
- **Name:** <%= argument[:name] %>,
25
- **Type:** <%= argument[:type] %>,
26
- **Units:** <%= argument[:units] %>,
27
- **Required:** <%= argument[:required] %>,
28
- **Model Dependent:** <%= argument[:model_dependent] %>
29
- <% end %>
30
-
31
- <% if arguments.size == 0 %>
32
- <%= "This measure does not have any user arguments" %>
33
- <% end %>
34
-
35
- <% if outputs.size > 0 %>
36
- ## Outputs
37
- <% output_names = [] %>
38
- <% outputs.each do |output| %>
39
- <% output_names << output[:display_name] %>
40
- <% end %>
41
- <%= output_names.join(", ") %>
42
- <% end %>
@@ -1,957 +0,0 @@
1
- # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
- # contributors. All rights reserved.
4
- #
5
- # Redistribution and use in source and binary forms, with or without modification,
6
- # are permitted provided that the following conditions are met:
7
- #
8
- # Redistributions of source code must retain the above copyright notice, this list
9
- # of conditions and the following disclaimer.
10
- #
11
- # Redistributions in binary form must reproduce the above copyright notice, this
12
- # list of conditions and the following disclaimer in the documentation and/or other
13
- # materials provided with the distribution.
14
- #
15
- # Neither the name of the copyright holder nor the names of its contributors may be
16
- # used to endorse or promote products derived from this software without specific
17
- # prior written permission.
18
- #
19
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
- # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
- # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23
- # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24
- # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
- # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
- # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27
- # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28
- # OF THE POSSIBILITY OF SUCH DAMAGE.
29
- # *********************************************************************************
30
-
31
- require 'urbanopt/scenario/default_reports/feature_report'
32
- require 'csv'
33
- require 'benchmark'
34
- require 'logger'
35
-
36
- @@logger = Logger.new(STDOUT)
37
-
38
- # start the measure
39
- class DefaultFeatureReports < OpenStudio::Measure::ReportingMeasure
40
- # human readable name
41
- def name
42
- return 'DefaultFeatureReports'
43
- end
44
-
45
- # human readable description
46
- def description
47
- return 'Writes default_feature_reports.json and default_feature_reports.csv files used by URBANopt Scenario Default Post Processor'
48
- end
49
-
50
- # human readable description of modeling approach
51
- def modeler_description
52
- return 'This measure only allows for one feature_report per simulation. If multiple features are simulated in a single simulation, a new measure must be written to disaggregate simulation results to multiple features.'
53
- end
54
-
55
- # define the arguments that the user will input
56
- def arguments
57
- args = OpenStudio::Measure::OSArgumentVector.new
58
-
59
- id = OpenStudio::Measure::OSArgument.makeStringArgument('feature_id', false)
60
- id.setDisplayName('Feature unique identifier')
61
- id.setDefaultValue('1')
62
- args << id
63
-
64
- name = OpenStudio::Measure::OSArgument.makeStringArgument('feature_name', false)
65
- name.setDisplayName('Feature scenario specific name')
66
- name.setDefaultValue('name')
67
- args << name
68
-
69
- feature_type = OpenStudio::Measure::OSArgument.makeStringArgument('feature_type', false)
70
- feature_type.setDisplayName('URBANopt Feature Type')
71
- feature_type.setDefaultValue('Building')
72
- args << feature_type
73
-
74
- # make an argument for the frequency
75
- reporting_frequency_chs = OpenStudio::StringVector.new
76
- reporting_frequency_chs << 'Detailed'
77
- reporting_frequency_chs << 'Timestep'
78
- reporting_frequency_chs << 'Hourly'
79
- reporting_frequency_chs << 'Daily'
80
- # reporting_frequency_chs << 'Zone Timestep'
81
- reporting_frequency_chs << 'BillingPeriod' # match it to utility bill object
82
- ## Utility report here to report the start and end for each fueltype
83
- reporting_frequency_chs << 'Monthly'
84
- reporting_frequency_chs << 'Runperiod'
85
-
86
- reporting_frequency = OpenStudio::Measure::OSArgument.makeChoiceArgument('reporting_frequency', reporting_frequency_chs, true)
87
- reporting_frequency.setDisplayName('Reporting Frequency')
88
- reporting_frequency.setDescription('The frequency at which to report timeseries output data.')
89
- reporting_frequency.setDefaultValue('Timestep')
90
- args << reporting_frequency
91
-
92
- return args
93
- end
94
-
95
- # define fuel types
96
- def fuel_types
97
- fuel_types = [
98
- 'Electricity',
99
- 'Gas',
100
- 'AdditionalFuel',
101
- 'DistrictCooling',
102
- 'DistrictHeating',
103
- 'Water'
104
- ]
105
-
106
- return fuel_types
107
- end
108
-
109
- # define enduses
110
- def end_uses
111
- end_uses = [
112
- 'Heating',
113
- 'Cooling',
114
- 'InteriorLights',
115
- 'ExteriorLights',
116
- 'InteriorEquipment',
117
- 'ExteriorEquipment',
118
- 'Fans',
119
- 'Pumps',
120
- 'HeatRejection',
121
- 'Humidifier',
122
- 'HeatRecovery',
123
- 'WaterSystems',
124
- 'Refrigeration',
125
- 'Generators',
126
- 'Facility'
127
- ]
128
-
129
- return end_uses
130
- end
131
-
132
- # format datetime
133
- def format_datetime(date_time)
134
- date_time.tr!('-', '/')
135
- date_time.gsub!('Jan', '01')
136
- date_time.gsub!('Feb', '02')
137
- date_time.gsub!('Mar', '03')
138
- date_time.gsub!('Apr', '04')
139
- date_time.gsub!('May', '05')
140
- date_time.gsub!('Jun', '06')
141
- date_time.gsub!('Jul', '07')
142
- date_time.gsub!('Aug', '08')
143
- date_time.gsub!('Sep', '09')
144
- date_time.gsub!('Oct', '10')
145
- date_time.gsub!('Nov', '11')
146
- date_time.gsub!('Dec', '12')
147
- return date_time
148
- end
149
-
150
- # return a vector of IdfObject's to request EnergyPlus objects needed by the run method
151
- # rubocop:disable Naming/MethodName
152
- def energyPlusOutputRequests(runner, user_arguments)
153
- super(runner, user_arguments)
154
-
155
- result = OpenStudio::IdfObjectVector.new
156
-
157
- reporting_frequency = runner.getStringArgumentValue('reporting_frequency', user_arguments)
158
-
159
- # Request the output for each end use/fuel type combination
160
- end_uses.each do |end_use|
161
- fuel_types.each do |fuel_type|
162
- variable_name = if end_use == 'Facility'
163
- "#{fuel_type}:#{end_use}"
164
- else
165
- "#{end_use}:#{fuel_type}"
166
- end
167
- result << OpenStudio::IdfObject.load("Output:Meter,#{variable_name},#{reporting_frequency};").get
168
- end
169
- end
170
-
171
- # Request the output for each end use/fuel type combination
172
- result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Electricity:Facility,#{reporting_frequency};").get
173
- result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,ElectricityProduced:Facility,#{reporting_frequency};").get
174
- result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Gas:Facility,#{reporting_frequency};").get
175
- result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,DistrictCooling:Facility,#{reporting_frequency};").get
176
- result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,DistrictHeating:Facility,#{reporting_frequency};").get
177
-
178
- timeseries_data = ['District Cooling Chilled Water Rate', 'District Cooling Mass Flow Rate',
179
- 'District Cooling Inlet Temperature', 'District Cooling Outlet Temperature',
180
- 'District Heating Hot Water Rate', 'District Heating Mass Flow Rate',
181
- 'District Heating Inlet Temperature', 'District Heating Outlet Temperature']
182
-
183
- timeseries_data.each do |ts|
184
- result << OpenStudio::IdfObject.load("Output:Variable,*,#{ts},#{reporting_frequency};").get
185
- end
186
-
187
- # use the built-in error checking
188
- if !runner.validateUserArguments(arguments, user_arguments)
189
- return result
190
- end
191
-
192
- return result
193
- end
194
-
195
- # sql_query method
196
- def sql_query(runner, sql, report_name, query)
197
- val = nil
198
- result = sql.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='#{report_name}' AND #{query}")
199
- if result.empty?
200
- runner.registerWarning("Query failed for #{report_name} and #{query}")
201
- else
202
- begin
203
- val = result.get
204
- rescue StandardError
205
- val = nil
206
- runner.registerWarning('Query result.get failed')
207
- end
208
- end
209
-
210
- val
211
- end
212
-
213
- # unit conversion method
214
- def convert_units(value, from_units, to_units)
215
- # apply unit conversion
216
- value_converted = OpenStudio.convert(value, from_units, to_units)
217
- if value_converted.is_initialized
218
- value = value_converted.get
219
- else
220
- @runner.registerError("Was not able to convert #{value} from #{from_units} to #{to_units}.")
221
- value = nil
222
- end
223
- return value
224
- end
225
-
226
- # define what happens when the measure is run
227
- # rubocop:disable Metrics/AbcSize
228
- def run(runner, user_arguments)
229
- super(runner, user_arguments)
230
-
231
- # use the built-in error checking
232
- unless runner.validateUserArguments(arguments, user_arguments)
233
- return false
234
- end
235
-
236
- # use the built-in error checking
237
- if !runner.validateUserArguments(arguments, user_arguments)
238
- return false
239
- end
240
-
241
- feature_id = runner.getStringArgumentValue('feature_id', user_arguments)
242
- feature_name = runner.getStringArgumentValue('feature_name', user_arguments)
243
- feature_type = runner.getStringArgumentValue('feature_type', user_arguments)
244
-
245
- # Assign the user inputs to variables
246
- reporting_frequency = runner.getStringArgumentValue('reporting_frequency', user_arguments)
247
-
248
- # BilingPeriod reporting frequency not implemented yet
249
- if reporting_frequency == 'BillingPeriod'
250
- @@logger.error('BillingPeriod frequency is not implemented yet')
251
- end
252
-
253
- # cache runner for this instance of the measure
254
- @runner = runner
255
-
256
- # get the WorkflowJSON object
257
- workflow = runner.workflow
258
-
259
- # get the last model and sql file
260
- model = runner.lastOpenStudioModel
261
- if model.empty?
262
- runner.registerError('Cannot find last model.')
263
- return false
264
- end
265
- model = model.get
266
-
267
- sql_file = runner.lastEnergyPlusSqlFile
268
- if sql_file.empty?
269
- runner.registerError('Cannot find last sql file.')
270
- return false
271
- end
272
- sql_file = sql_file.get
273
- model.setSqlFile(sql_file)
274
-
275
- # get building from model
276
- building = model.getBuilding
277
-
278
- # get surfaces from model
279
- surfaces = model.getSurfaces
280
-
281
- # get epw_file
282
- epw_file = runner.lastEpwFile
283
- if epw_file.empty?
284
- runner.registerError('Cannot find last epw file.')
285
- return false
286
- end
287
- epw_file = epw_file.get
288
-
289
- # create output feature_report report object
290
- feature_report = URBANopt::Scenario::DefaultReports::FeatureReport.new
291
- feature_report.id = feature_id
292
- feature_report.name = feature_name
293
- feature_report.feature_type = feature_type
294
- feature_report.directory_name = workflow.absoluteRunDir
295
-
296
- timesteps_per_hour = model.getTimestep.numberOfTimestepsPerHour
297
- feature_report.timesteps_per_hour = timesteps_per_hour
298
-
299
- feature_report.simulation_status = 'Complete'
300
-
301
- feature_report.reporting_periods << URBANopt::Scenario::DefaultReports::ReportingPeriod.new
302
-
303
- ###########################################################################
304
- ##
305
- # Get Location information and store in the feature_report
306
- ##
307
-
308
- # latitude
309
- latitude = epw_file.latitude
310
- feature_report.location.latitude = latitude
311
-
312
- # longitude
313
- longitude = epw_file.longitude
314
- feature_report.location.longitude = longitude
315
-
316
- # surface_elevation
317
- elev = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='General' AND RowName='Elevation' AND ColumnName='Value'")
318
- feature_report.location.surface_elevation = elev
319
-
320
- ##########################################################################
321
- ##
322
- # Get program information and store in the feature_report
323
- ##
324
-
325
- # floor_area
326
- floor_area = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Total Building Area' AND ColumnName='Area'")
327
- feature_report.program.floor_area = convert_units(floor_area, 'm^2', 'ft^2')
328
-
329
- # conditioned_area
330
- conditioned_area = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Net Conditioned Building Area' AND ColumnName='Area'")
331
- feature_report.program.conditioned_area = convert_units(conditioned_area, 'm^2', 'ft^2')
332
-
333
- # unconditioned_area
334
- unconditioned_area = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Unconditioned Building Area' AND ColumnName='Area'")
335
- feature_report.program.unconditioned_area = convert_units(unconditioned_area, 'm^2', 'ft^2')
336
-
337
- # footprint_area
338
- feature_report.program.footprint_area = convert_units(floor_area, 'm^2', 'ft^2')
339
-
340
- # maximum_number_of_stories
341
- number_of_stories = building.standardsNumberOfStories.get if building.standardsNumberOfStories.is_initialized
342
- number_of_stories ||= 1
343
- feature_report.program.maximum_number_of_stories = number_of_stories
344
-
345
- # maximum_roof_height
346
- floor_to_floor_height = building.nominalFloortoFloorHeight.to_f
347
- maximum_roof_height = number_of_stories * floor_to_floor_height
348
- feature_report.program.maximum_roof_height = maximum_roof_height
349
-
350
- # maximum_number_of_stories_above_ground
351
- number_of_stories_above_ground = building.standardsNumberOfAboveGroundStories.get if building.standardsNumberOfAboveGroundStories.is_initialized
352
- number_of_stories_above_ground ||= 1
353
- feature_report.program.maximum_number_of_stories_above_ground = number_of_stories_above_ground
354
-
355
- # number_of_residential_units
356
- number_of_living_units = building.standardsNumberOfLivingUnits.to_i.get if building.standardsNumberOfLivingUnits.is_initialized
357
- number_of_living_units ||= 1
358
- feature_report.program.number_of_residential_units = number_of_living_units
359
-
360
- ## building_types
361
-
362
- # get an array of the model spaces
363
- spaces = model.getSpaces
364
-
365
- # get array of model space types
366
- space_types = model.getSpaceTypes
367
-
368
- # create a hash for space_type_areas (spcace types as keys and their areas as values)
369
- space_type_areas = {}
370
- model.getSpaceTypes.each do |space_type|
371
- building_type = space_type.standardsBuildingType
372
- if building_type.empty?
373
- building_type = 'unknown'
374
- else
375
- building_type = building_type.get
376
- end
377
- space_type_areas[building_type] = 0 if space_type_areas[building_type].nil?
378
- space_type_areas[building_type] += convert_units(space_type.floorArea, 'm^2', 'ft^2')
379
- end
380
-
381
- # create a hash for space_type_occupancy (spcace types as keys and their occupancy as values)
382
- space_type_occupancy = {}
383
- spaces.each do |space|
384
- if space.spaceType.empty?
385
- raise 'space.spaceType is empty. Make sure spaces have a space type'
386
- else
387
- building_type = space.spaceType.get.standardsBuildingType
388
- end
389
- if building_type.empty?
390
- building_type = 'unknown'
391
- else
392
- building_type = building_type.get
393
- end
394
- space_type_occupancy[building_type] = 0 if space_type_occupancy[building_type].nil?
395
- space_type_occupancy[building_type] += space.numberOfPeople
396
- end
397
-
398
- # combine all in a building_types array
399
- building_types = []
400
- for i in 0..(space_type_areas.size - 1)
401
- building_types << { building_type: space_type_areas.keys[i], floor_area: space_type_areas.values[i], maximum_occupancy: space_type_occupancy.values[i] }
402
- end
403
- # add results to the feature report JSON
404
- feature_report.program.building_types = building_types
405
-
406
- ## window_area
407
- # north_window_area
408
- north_window_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Window Opening Area' AND ColumnName='North (315 to 45 deg)'").to_f
409
- feature_report.program.window_area[:north_window_area] = convert_units(north_window_area, 'm^2', 'ft^2')
410
- # south_window_area
411
- south_window_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Window Opening Area' AND ColumnName='South (135 to 225 deg)'").to_f
412
- feature_report.program.window_area[:south_window_area] = convert_units(south_window_area, 'm^2', 'ft^2')
413
- # east_window_area
414
- east_window_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Window Opening Area' AND ColumnName='East (45 to 135 deg)'").to_f
415
- feature_report.program.window_area[:east_window_area] = convert_units(east_window_area, 'm^2', 'ft^2')
416
- # west_window_area
417
- west_window_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Window Opening Area' AND ColumnName='West (225 to 315 deg)'").to_f
418
- feature_report.program.window_area[:west_window_area] = convert_units(west_window_area, 'm^2', 'ft^2')
419
- # total_window_area
420
- total_window_area = north_window_area + south_window_area + east_window_area + west_window_area
421
- feature_report.program.window_area[:total_window_area] = convert_units(total_window_area, 'm^2', 'ft^2')
422
-
423
- ## wall_area
424
- # north_wall_area
425
- north_wall_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Gross Wall Area' AND ColumnName='North (315 to 45 deg)'").to_f
426
- feature_report.program.wall_area[:north_wall_area] = convert_units(north_wall_area, 'm^2', 'ft^2')
427
- # south_wall_area
428
- south_wall_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Gross Wall Area' AND ColumnName='South (135 to 225 deg)'").to_f
429
- feature_report.program.wall_area[:south_wall_area] = convert_units(south_wall_area, 'm^2', 'ft^2')
430
- # east_wall_area
431
- east_wall_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Gross Wall Area' AND ColumnName='East (45 to 135 deg)'").to_f
432
- feature_report.program.wall_area[:east_wall_area] = convert_units(east_wall_area, 'm^2', 'ft^2')
433
- # west_wall_area
434
- west_wall_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Gross Wall Area' AND ColumnName='West (225 to 315 deg)'").to_f
435
- feature_report.program.wall_area[:west_wall_area] = convert_units(west_wall_area, 'm^2', 'ft^2')
436
- # total_wall_area
437
- total_wall_area = north_wall_area + south_wall_area + east_wall_area + west_wall_area
438
- feature_report.program.wall_area[:total_wall_area] = convert_units(total_wall_area, 'm^2', 'ft^2')
439
-
440
- # total_roof_area
441
- total_roof_area = 0.0
442
- surfaces.each do |surface|
443
- if (surface.outsideBoundaryCondition == 'Outdoors') && (surface.surfaceType == 'RoofCeiling')
444
- total_roof_area += surface.netArea
445
- end
446
- end
447
- feature_report.program.roof_area[:total_roof_area] = convert_units(total_roof_area, 'm^2', 'ft^2')
448
-
449
- # orientation
450
- # RK: a more robust method should be implemented to find orientation(finding main axis of the building using aspect ratio)
451
- building_rotation = model.getBuilding.northAxis
452
- feature_report.program.orientation = building_rotation
453
-
454
- # aspect_ratio
455
- north_wall_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Gross Wall Area' AND ColumnName='North (315 to 45 deg)'")
456
- east_wall_area = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='Window-Wall Ratio' AND RowName='Gross Wall Area' AND ColumnName='East (45 to 135 deg)'")
457
- aspect_ratio = north_wall_area / east_wall_area if north_wall_area != 0 && east_wall_area != 0
458
- aspect_ratio ||= nil
459
- feature_report.program.aspect_ratio = aspect_ratio
460
-
461
- # total_construction_cost
462
- total_construction_cost = sql_query(runner, sql_file, 'Life-Cycle Cost Report', "TableName='Present Value for Recurring, Nonrecurring and Energy Costs (Before Tax)' AND RowName='LCC_MAT - BUILDING - LIFE CYCLE COSTS' AND ColumnName='Cost'")
463
- feature_report.program.total_construction_cost = total_construction_cost
464
-
465
- ############################################################################
466
- ##
467
- # Get Reporting Periods information and store in the feature_report
468
- ##
469
-
470
- # start_date
471
- # month
472
- begin_month = model.getRunPeriod.getBeginMonth
473
- feature_report.reporting_periods[0].start_date.month = begin_month
474
- # day_of_month
475
- begin_day_of_month = model.getRunPeriod.getBeginDayOfMonth
476
- feature_report.reporting_periods[0].start_date.day_of_month = begin_day_of_month
477
- # year
478
- begin_year = model.getYearDescription.calendarYear
479
- feature_report.reporting_periods[0].start_date.year = begin_year
480
-
481
- # end_date
482
- # month
483
- end_month = model.getRunPeriod.getEndMonth
484
- feature_report.reporting_periods[0].end_date.month = end_month
485
- # day_of_month
486
- end_day_of_month = model.getRunPeriod.getEndDayOfMonth
487
- feature_report.reporting_periods[0].end_date.day_of_month = end_day_of_month
488
- # year
489
- end_year = model.getYearDescription.calendarYear
490
- feature_report.reporting_periods[0].end_date.year = end_year
491
-
492
- # total_site_energy
493
- total_site_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Total Site Energy' AND ColumnName='Total Energy'")
494
- feature_report.reporting_periods[0].total_site_energy = convert_units(total_site_energy, 'GJ', 'kBtu')
495
-
496
- # total_source_energy
497
- total_source_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Total Source Energy' AND ColumnName='Total Energy'")
498
- feature_report.reporting_periods[0].total_source_energy = convert_units(total_source_energy, 'GJ', 'kBtu')
499
-
500
- # net_site_energy
501
- net_site_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Net Site Energy' AND ColumnName='Total Energy'")
502
- feature_report.reporting_periods[0].net_site_energy = convert_units(net_site_energy, 'GJ', 'kBtu')
503
-
504
- # net_source_energy
505
- net_source_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Net Source Energy' AND ColumnName='Total Energy'")
506
- feature_report.reporting_periods[0].net_source_energy = convert_units(net_source_energy, 'GJ', 'kBtu')
507
-
508
- # electricity
509
- electricity = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Electricity'")
510
- feature_report.reporting_periods[0].electricity = convert_units(electricity, 'GJ', 'kBtu')
511
-
512
- # natural_gas
513
- natural_gas = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Natural Gas'")
514
- feature_report.reporting_periods[0].natural_gas = convert_units(natural_gas, 'GJ', 'kBtu')
515
-
516
- # additional_fuel
517
- additional_fuel = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Additional Fuel'")
518
- feature_report.reporting_periods[0].additional_fuel = convert_units(additional_fuel, 'GJ', 'kBtu')
519
-
520
- # district_cooling
521
- district_cooling = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='District Cooling'")
522
- feature_report.reporting_periods[0].district_cooling = convert_units(district_cooling, 'GJ', 'kBtu')
523
-
524
- # district_heating
525
- district_heating = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='District Heating'")
526
- feature_report.reporting_periods[0].district_heating = convert_units(district_heating, 'GJ', 'kBtu')
527
-
528
- # water
529
- water = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Water'")
530
- # feature_report.reporting_periods[0].water = convert_units(water, 'm3', 'ft3')
531
- feature_report.reporting_periods[0].water = water
532
-
533
- # electricity_produced
534
- electricity_produced = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Electric Loads Satisfied' AND RowName='Total On-Site and Utility Electric Sources' AND ColumnName='Electricity'")
535
- feature_report.reporting_periods[0].electricity_produced = convert_units(electricity_produced, 'GJ', 'kBtu')
536
-
537
- ## end_uses
538
-
539
- # get fuel type as listed in the sql file
540
- fuel_type = ['Electricity', 'Natural Gas', 'Additional Fuel', 'District Cooling', 'District Heating', 'Water']
541
-
542
- # get enduses as listed in the sql file
543
- enduses = ['Heating', 'Cooling', 'Interior Lighting', 'Exterior Lighting', 'Interior Equipment', 'Exterior Equipment', 'Fans', 'Pumps',
544
- 'Heat Rejection', 'Humidification', 'Heat Recovery', 'Water Systems', 'Refrigeration', 'Generators']
545
-
546
- # loop through fuel types and enduses to fill in sql_query method
547
- fuel_type.each do |ft|
548
- enduses.each do |eu|
549
- sql_r = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='#{eu}' AND ColumnName='#{ft}'")
550
-
551
- # report each query in its corresponding feature report obeject
552
- if ft.include? ' '
553
- x = ft.tr(' ', '_').downcase
554
- m = feature_report.reporting_periods[0].end_uses.send(x)
555
- else
556
- m = feature_report.reporting_periods[0].end_uses.send(ft.downcase)
557
-
558
- end
559
-
560
- if eu.include? ' '
561
- y = eu.tr(' ', '_').downcase
562
- m.send("#{y}=", convert_units(sql_r, 'GJ', 'kBtu'))
563
- else
564
- m.send("#{eu.downcase}=", convert_units(sql_r, 'GJ', 'kBtu'))
565
- end
566
- end
567
- end
568
-
569
- ### energy_production
570
- ## electricity_produced
571
- # photovoltaic
572
- photovoltaic_power = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Electric Loads Satisfied' AND RowName='Photovoltaic Power' AND ColumnName='Electricity'")
573
- feature_report.reporting_periods[0].energy_production[:electricity_produced][:photovoltaic] = convert_units(photovoltaic_power, 'GJ', 'kBtu')
574
-
575
- ## comfort_result
576
- # time_setpoint_not_met_during_occupied_cooling
577
- time_setpoint_not_met_during_occupied_cooling = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Comfort and Setpoint Not Met Summary' AND RowName='Time Setpoint Not Met During Occupied Cooling' AND ColumnName='Facility'")
578
- feature_report.reporting_periods[0].comfort_result[:time_setpoint_not_met_during_occupied_cooling] = time_setpoint_not_met_during_occupied_cooling
579
-
580
- # time_setpoint_not_met_during_occupied_heating
581
- time_setpoint_not_met_during_occupied_heating = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Comfort and Setpoint Not Met Summary' AND RowName='Time Setpoint Not Met During Occupied Heating' AND ColumnName='Facility'")
582
- feature_report.reporting_periods[0].comfort_result[:time_setpoint_not_met_during_occupied_heating] = time_setpoint_not_met_during_occupied_heating
583
-
584
- # time_setpoint_not_met_during_occupied_hour
585
- time_setpoint_not_met_during_occupied_hours = time_setpoint_not_met_during_occupied_heating + time_setpoint_not_met_during_occupied_cooling
586
- feature_report.reporting_periods[0].comfort_result[:time_setpoint_not_met_during_occupied_hours] = time_setpoint_not_met_during_occupied_hours
587
-
588
- ######################################## Reporting TImeseries Results FOR CSV File ######################################
589
-
590
- # Get the weather file run period (as opposed to design day run period)
591
- ann_env_pd = nil
592
- sql_file.availableEnvPeriods.each do |env_pd|
593
- env_type = sql_file.environmentType(env_pd)
594
- if env_type.is_initialized
595
- if env_type.get == OpenStudio::EnvironmentType.new('WeatherRunPeriod')
596
- ann_env_pd = env_pd
597
- end
598
- end
599
- end
600
-
601
- if ann_env_pd == false
602
- runner.registerError("Can't find a weather runperiod, make sure you ran an annual simulation, not just the design days.")
603
- return false
604
- end
605
-
606
- # timeseries we want to report
607
- requested_timeseries_names = [
608
- 'Electricity:Facility',
609
- 'ElectricityProduced:Facility',
610
- 'Gas:Facility',
611
- 'DistrictCooling:Facility',
612
- 'DistrictHeating:Facility',
613
- 'District Cooling Chilled Water Rate',
614
- 'District Cooling Mass Flow Rate',
615
- 'District Cooling Inlet Temperature',
616
- 'District Cooling Outlet Temperature',
617
- 'District Heating Hot Water Rate',
618
- 'District Heating Mass Flow Rate',
619
- 'District Heating Inlet Temperature',
620
- 'District Heating Outlet Temperature'
621
- ]
622
-
623
- # add thermal comfort timeseries
624
- comfortTimeseries = ['Zone Thermal Comfort Fanger Model PMV', 'Zone Thermal Comfort Fanger Model PPD']
625
- requested_timeseries_names += comfortTimeseries
626
-
627
- # add additional power timeseries (for calculating transformer apparent power to compare to rating ) in VA
628
- powerTimeseries = ['Net Electric Energy', 'Electricity:Facility Power', 'ElectricityProduced:Facility Power', 'Electricity:Facility Apparent Power', 'ElectricityProduced:Facility Apparent Power', 'Net Power', 'Net Apparent Power']
629
- requested_timeseries_names += powerTimeseries
630
-
631
- # register info all timeseries
632
- runner.registerInfo("All timeseries: #{requested_timeseries_names}")
633
-
634
- # timeseries variables to keep to calculate power
635
- tsToKeep = ['Electricity:Facility', 'ElectricityProduced:Facility']
636
- tsToKeepIndexes = {}
637
-
638
- ### powerFactor ###
639
- # use power_factor default: 0.9
640
- # TODO: Set powerFactor default based on building type
641
- powerFactor = 0.9
642
-
643
- ### power_conversion ###
644
- # divide values by total_seconds to convert J to W (W = J/sec)
645
- # divide values by total_hours to convert kWh to kW (kW = kWh/hrs)
646
- total_seconds = (60 / timesteps_per_hour.to_f) * 60 # make sure timesteps_per_hour is a float in the division
647
- total_hours = 1 / timesteps_per_hour.to_f # make sure timesteps_per_hour is a float in the division
648
- # set power_conversion
649
- power_conversion = total_hours
650
- puts "Power Converion: to convert kWh to kW values will be divided by #{power_conversion}"
651
-
652
- # number of values in each timeseries
653
- n = nil
654
- # all numeric timeseries values, transpose of CSV file (e.g. values[key_cnt] is column, values[key_cnt][i] is column and row)
655
- values = []
656
- tmpArray = []
657
- # since schedule value will have a bunch of key_values, we need to keep track of these as additional timeseries
658
- key_cnt = 0
659
- # this is recording the name of these final timeseries to write in the header of the CSV
660
- final_timeseries_names = []
661
-
662
- # loop over requested timeseries
663
- # rubocop: disable Metrics/BlockLength
664
- requested_timeseries_names.each_index do |i|
665
- timeseries_name = requested_timeseries_names[i]
666
- runner.registerInfo("TIMESERIES: #{timeseries_name}")
667
-
668
- # get all the key values that this timeseries can be reported for (e.g. if PMV is requested for each zone)
669
- key_values = sql_file.availableKeyValues('RUN PERIOD 1', 'Zone Timestep', timeseries_name)
670
- runner.registerInfo("KEY VALUES: #{key_values}")
671
- if key_values.empty?
672
- key_values = ['']
673
- end
674
-
675
- # sort keys
676
- sorted_keys = key_values.sort
677
- requested_keys = requested_timeseries_names
678
- final_keys = []
679
- # make sure aggregated timeseries are listed in sorted order before all individual feature timeseries
680
- sorted_keys.each do |k|
681
- if requested_keys.include? k
682
- final_keys << k
683
- end
684
- end
685
- sorted_keys.each do |k|
686
- if !requested_keys.include? k
687
- final_keys << k
688
- end
689
- end
690
-
691
- # loop over final keys
692
- final_keys.each_with_index do |key_value, key_i|
693
- new_timeseries_name = ''
694
-
695
- runner.registerInfo("!! TIMESERIES NAME: #{timeseries_name} AND key_value: #{key_value}")
696
-
697
- # check if we have to come up with a new name for the timeseries in our CSV header
698
- if key_values.size == 1
699
- # use timeseries name when only 1 keyvalue
700
- new_timeseries_name = timeseries_name
701
- else
702
- # use key_value name
703
- # special case for Zone Thermal Comfort: use both timeseries_name and key_value
704
- if timeseries_name.include? 'Zone Thermal Comfort'
705
- new_timeseries_name = timeseries_name + ' ' + key_value
706
- else
707
- new_timeseries_name = key_value
708
- end
709
- end
710
- # final_timeseries_names << new_timeseries_name
711
-
712
- # get the actual timeseries
713
- ts = sql_file.timeSeries(ann_env_pd.to_s, reporting_frequency.to_s, timeseries_name, key_value)
714
-
715
- if n.nil?
716
- # first timeseries should always be set
717
- runner.registerInfo('First timeseries')
718
- values[key_cnt] = ts.get.values
719
- n = values[key_cnt].size
720
- elsif ts.is_initialized
721
- runner.registerInfo('Is Initialized')
722
- values[key_cnt] = ts.get.values
723
- else
724
- runner.registerInfo('Is NOT Initialized')
725
- values[key_cnt] = Array.new(n, 0)
726
- end
727
-
728
- # unit conversion
729
- old_unit = ts.get.units if ts.is_initialized
730
-
731
- if timeseries_name.include? 'Gas'
732
- new_unit = 'kBtu'
733
- else
734
- new_unit = case old_unit.to_s
735
- when 'J'
736
- 'kWh'
737
- when 'kBtu'
738
- 'kWh'
739
- when 'gal'
740
- 'm3'
741
- end
742
- end
743
- # loop through each value and apply unit conversion
744
- os_vec = values[key_cnt]
745
- if !timeseries_name.include? 'Zone Thermal Comfort'
746
- for i in 0..os_vec.length - 1
747
- unless new_unit == old_unit || !ts.is_initialized
748
- os_vec[i] = OpenStudio.convert(os_vec[i], old_unit, new_unit).get
749
- end
750
- end
751
- end
752
-
753
- # keep certain timeseries to calculate power
754
- if tsToKeep.include? timeseries_name
755
- tsToKeepIndexes[timeseries_name] = key_cnt
756
- end
757
-
758
- # special processing: power
759
- if powerTimeseries.include? timeseries_name
760
- # special case: net series (subtract generation from load)
761
- if timeseries_name.include? 'Net'
762
-
763
- newVals = Array.new(n, 0)
764
- # Apparent power calculation
765
-
766
- if timeseries_name.include?('Apparent')
767
- (0..n - 1).each do |j|
768
- newVals[j] = (values[tsToKeepIndexes['Electricity:Facility']][j].to_f - values[tsToKeepIndexes['ElectricityProduced:Facility']][j].to_f) / power_conversion / powerFactor
769
- j += 1
770
- end
771
- new_unit = 'kW'
772
- elsif timeseries_name.include? 'Net Electric Energy'
773
- (0..n - 1).each do |j|
774
- newVals[j] = (values[tsToKeepIndexes['Electricity:Facility']][j].to_f - values[tsToKeepIndexes['ElectricityProduced:Facility']][j].to_f)
775
- j += 1
776
- end
777
- new_unit = 'kWh'
778
- else
779
- runner.registerInfo('Power calc')
780
- # Power calculation
781
- (0..n - 1).each do |j|
782
- newVals[j] = (values[tsToKeepIndexes['Electricity:Facility']][j].to_f - values[tsToKeepIndexes['ElectricityProduced:Facility']][j].to_f) / power_conversion
783
- j += 1
784
- end
785
- new_unit = 'kW'
786
- end
787
-
788
- values[key_cnt] = newVals
789
- else
790
- tsToKeepIndexes.each do |key, indexValue|
791
- if timeseries_name.include? key
792
- runner.registerInfo("timeseries_name: #{timeseries_name}, key: #{key}")
793
- # use this timeseries
794
- newVals = Array.new(n, 0)
795
- # Apparent power calculation
796
- if timeseries_name.include?('Apparent')
797
- (0..n - 1).each do |j|
798
- newVals[j] = values[indexValue][j].to_f / power_conversion / powerFactor
799
- j += 1
800
- end
801
- new_unit = 'kW'
802
- else
803
- # Power calculation
804
- (0..n - 1).each do |j|
805
- newVals[j] = values[indexValue][j].to_f / power_conversion
806
- j += 1
807
- end
808
- new_unit = 'kW'
809
- end
810
- values[key_cnt] = newVals
811
- end
812
- end
813
- end
814
- end
815
-
816
- # append units to headers
817
- new_timeseries_name += "(#{new_unit})"
818
- final_timeseries_names << new_timeseries_name
819
-
820
- # TODO: DELETE PUTS
821
- # puts " *********timeseries_name = #{timeseries_name}******************"
822
- # if timeseries_name.include? 'Power'
823
- # puts "values = #{values[key_cnt]}"
824
- # puts "units = #{new_unit}"
825
- # end
826
-
827
- # comfort results usually have multiple timeseries (per zone), aggregate into a single series with consistent name and use worst value at each timestep
828
- if comfortTimeseries.include? timeseries_name
829
-
830
- # set up array if 1st key_value
831
- if key_i == 0
832
- runner.registerInfo("SETTING UP NEW ARRAY FOR: #{timeseries_name}")
833
- tmpArray = Array.new(n, 0)
834
- end
835
-
836
- # add to array (keep max value at each timestep)
837
- (0..(n - 1)).each do |ind|
838
- # process negative and positive values differently
839
- tVal = values[key_cnt][ind].to_f
840
- if tVal < 0
841
- tmpArray[ind] = [tVal, tmpArray[ind]].min
842
- else
843
- tmpArray[ind] = [tVal, tmpArray[ind]].max
844
- end
845
- end
846
-
847
- # aggregate and save when all keyvalues have been processed
848
- if key_i == final_keys.size - 1
849
-
850
- hrsOutOfBounds = 0
851
- if timeseries_name === 'Zone Thermal Comfort Fanger Model PMV'
852
- (0..(n - 1)).each do |ind|
853
- # -0.5 < x < 0.5 is within bounds
854
- if values[key_cnt][ind].to_f > 0.5 || values[key_cnt][ind].to_f < -0.5
855
- hrsOutOfBounds += 1
856
- end
857
- end
858
- hrsOutOfBounds = hrsOutOfBounds.to_f / timesteps_per_hour
859
- elsif timeseries_name === 'Zone Thermal Comfort Fanger Model PPD'
860
- (0..(n - 1)).each do |ind|
861
- # > 20 is outside bounds
862
- if values[key_cnt][ind].to_f > 20
863
- hrsOutOfBounds += 1
864
- end
865
- end
866
- hrsOutOfBounds = hrsOutOfBounds.to_f / timesteps_per_hour
867
- else
868
- # this one is already scaled by timestep, no need to divide total
869
- (0..(n - 1)).each do |ind|
870
- hrsOutOfBounds += values[key_cnt][ind].to_f if values[key_cnt][ind].to_f > 0
871
- end
872
- end
873
-
874
- # save variable to feature_reports hash
875
- runner.registerInfo("timeseries #{timeseries_name}: hours out of bounds: #{hrsOutOfBounds}")
876
- if timeseries_name === 'Zone Thermal Comfort Fanger Model PMV'
877
- feature_report.reporting_periods[0].comfort_result[:hours_out_of_comfort_bounds_PMV] = hrsOutOfBounds
878
- elsif timeseries_name == 'Zone Thermal Comfort Fanger Model PPD'
879
- feature_report.reporting_periods[0].comfort_result[:hours_out_of_comfort_bounds_PPD] = hrsOutOfBounds
880
- end
881
-
882
- end
883
-
884
- end
885
-
886
- # increment key_cnt in new_keys loop
887
- key_cnt += 1
888
- end
889
- end
890
-
891
- # Add datime column
892
- datetimes = []
893
- # check what timeseries is available
894
- available_ts = sql_file.availableTimeSeries
895
- # get the timeseries for any of available timeseries
896
- ts_d = sql_file.timeSeries(ann_env_pd.to_s, reporting_frequency.to_s, available_ts[0], '')
897
- timeseries_d = ts_d.get
898
- # get formated datetimes
899
- timeseries_d.dateTimes.each do |datetime|
900
- datetimes << format_datetime(datetime.to_s)
901
- end
902
- # insert datetimes to values
903
- values.insert(0, datetimes)
904
- # insert datetime header to names
905
- final_timeseries_names.insert(0, 'Datetime')
906
-
907
- # rubocop: enable Metrics/BlockLength
908
- runner.registerInfo("new final_timeseries_names size: #{final_timeseries_names.size}")
909
-
910
- # Save the 'default_feature_reports.csv' file
911
- File.open('default_feature_reports.csv', 'w') do |file|
912
- file.puts(final_timeseries_names.join(','))
913
- (0...n).each do |l|
914
- line = []
915
- values.each_index do |j|
916
- line << values[j][l]
917
- end
918
- file.puts(line.join(','))
919
- end
920
- end
921
-
922
- # closing the sql file
923
- sql_file.close
924
-
925
- ############################# Adding timeseries_csv info to json report and saving CSV ################################
926
- # add csv info to feature_report
927
- feature_report.timeseries_csv.path = File.join(Dir.pwd, 'default_feature_reports.csv')
928
- feature_report.timeseries_csv.first_report_datetime = '0'
929
- feature_report.timeseries_csv.column_names = final_timeseries_names
930
-
931
- ##### Save the 'default_feature_reports.json' file
932
-
933
- feature_report_hash = feature_report.to_hash
934
-
935
- File.open('default_feature_reports.json', 'w') do |f|
936
- f.puts JSON.pretty_generate(feature_report_hash)
937
- # make sure data is written to the disk one way or the other
938
- begin
939
- f.fsync
940
- rescue StandardError
941
- f.flush
942
- end
943
- end
944
-
945
- # reporting final condition
946
- runner.registerFinalCondition('Default Feature Reports generated successfully.')
947
-
948
- true
949
- # end the run method
950
- end
951
- # end the measure
952
- end
953
- # rubocop:enable Metrics/AbcSize
954
- # rubocop:enable Naming/MethodName
955
-
956
- # register the measure to be used by the application
957
- DefaultFeatureReports.new.registerWithApplication