urbanopt-scenario 0.2.0 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) 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 +53 -0
  6. data/CONTRIBUTING.md +1 -1
  7. data/Gemfile +27 -14
  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 +2 -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 +110 -8
  26. data/lib/urbanopt/scenario/scenario_post_processor_opendss.rb +6 -7
  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 +14 -14
  36. metadata +71 -77
  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 -979
  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 -379
  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 -267
  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/power_distribution.rb +0 -102
  62. data/lib/urbanopt/scenario/default_reports/program.rb +0 -266
  63. data/lib/urbanopt/scenario/default_reports/reporting_period.rb +0 -301
  64. data/lib/urbanopt/scenario/default_reports/scenario_report.rb +0 -317
  65. data/lib/urbanopt/scenario/default_reports/schema/README.md +0 -33
  66. data/lib/urbanopt/scenario/default_reports/schema/scenario_csv_columns.txt +0 -32
  67. data/lib/urbanopt/scenario/default_reports/schema/scenario_schema.json +0 -853
  68. data/lib/urbanopt/scenario/default_reports/solar_pv.rb +0 -93
  69. data/lib/urbanopt/scenario/default_reports/storage.rb +0 -105
  70. data/lib/urbanopt/scenario/default_reports/timeseries_csv.rb +0 -299
  71. data/lib/urbanopt/scenario/default_reports/validator.rb +0 -97
  72. 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,979 +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
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Cooling:Electricity,#{reporting_frequency};").get
178
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Heating:Electricity,#{reporting_frequency};").get
179
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,InteriorLights:Electricity,#{reporting_frequency};").get
180
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,ExteriorLights:Electricity,#{reporting_frequency};").get
181
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,InteriorEquipment:Electricity,#{reporting_frequency};").get
182
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Fans:Electricity,#{reporting_frequency};").get
183
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Pumps:Electricity,#{reporting_frequency};").get
184
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,WaterSystems:Electricity,#{reporting_frequency};").get
185
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,Heating:Gas,#{reporting_frequency};").get
186
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,WaterSystems:Gas,#{reporting_frequency};").get
187
- # result << OpenStudio::IdfObject.load("Output:Meter:MeterFileOnly,InteriorEquipment:Gas,#{reporting_frequency};").get
188
-
189
- timeseries_data = ['District Cooling Chilled Water Rate', 'District Cooling Mass Flow Rate',
190
- 'District Cooling Inlet Temperature', 'District Cooling Outlet Temperature',
191
- 'District Heating Hot Water Rate', 'District Heating Mass Flow Rate',
192
- 'District Heating Inlet Temperature', 'District Heating Outlet Temperature']
193
-
194
- timeseries_data.each do |ts|
195
- result << OpenStudio::IdfObject.load("Output:Variable,*,#{ts},#{reporting_frequency};").get
196
- end
197
-
198
- # use the built-in error checking
199
- if !runner.validateUserArguments(arguments, user_arguments)
200
- return result
201
- end
202
-
203
- return result
204
- end
205
-
206
- # sql_query method
207
- def sql_query(runner, sql, report_name, query)
208
- val = nil
209
- result = sql.execAndReturnFirstDouble("SELECT Value FROM TabularDataWithStrings WHERE ReportName='#{report_name}' AND #{query}")
210
- if result.empty?
211
- runner.registerWarning("Query failed for #{report_name} and #{query}")
212
- else
213
- begin
214
- val = result.get
215
- rescue StandardError
216
- val = nil
217
- runner.registerWarning('Query result.get failed')
218
- end
219
- end
220
-
221
- val
222
- end
223
-
224
- # unit conversion method
225
- def convert_units(value, from_units, to_units)
226
- # apply unit conversion
227
- value_converted = OpenStudio.convert(value, from_units, to_units)
228
- if value_converted.is_initialized
229
- value = value_converted.get
230
- else
231
- @runner.registerError("Was not able to convert #{value} from #{from_units} to #{to_units}.")
232
- value = nil
233
- end
234
- return value
235
- end
236
-
237
- # define what happens when the measure is run
238
- # rubocop:disable Metrics/AbcSize
239
- def run(runner, user_arguments)
240
- super(runner, user_arguments)
241
-
242
- # use the built-in error checking
243
- unless runner.validateUserArguments(arguments, user_arguments)
244
- return false
245
- end
246
-
247
- # use the built-in error checking
248
- if !runner.validateUserArguments(arguments, user_arguments)
249
- return false
250
- end
251
-
252
- feature_id = runner.getStringArgumentValue('feature_id', user_arguments)
253
- feature_name = runner.getStringArgumentValue('feature_name', user_arguments)
254
- feature_type = runner.getStringArgumentValue('feature_type', user_arguments)
255
-
256
- # Assign the user inputs to variables
257
- reporting_frequency = runner.getStringArgumentValue('reporting_frequency', user_arguments)
258
-
259
- # BilingPeriod reporting frequency not implemented yet
260
- if reporting_frequency == 'BillingPeriod'
261
- @@logger.error('BillingPeriod frequency is not implemented yet')
262
- end
263
-
264
- # cache runner for this instance of the measure
265
- @runner = runner
266
-
267
- # get the WorkflowJSON object
268
- workflow = runner.workflow
269
-
270
- # get the last model and sql file
271
- model = runner.lastOpenStudioModel
272
- if model.empty?
273
- runner.registerError('Cannot find last model.')
274
- return false
275
- end
276
- model = model.get
277
-
278
- sql_file = runner.lastEnergyPlusSqlFile
279
- if sql_file.empty?
280
- runner.registerError('Cannot find last sql file.')
281
- return false
282
- end
283
- sql_file = sql_file.get
284
- model.setSqlFile(sql_file)
285
-
286
- # get building from model
287
- building = model.getBuilding
288
-
289
- # get surfaces from model
290
- surfaces = model.getSurfaces
291
-
292
- # get epw_file
293
- epw_file = runner.lastEpwFile
294
- if epw_file.empty?
295
- runner.registerError('Cannot find last epw file.')
296
- return false
297
- end
298
- epw_file = epw_file.get
299
-
300
- # create output feature_report report object
301
- feature_report = URBANopt::Scenario::DefaultReports::FeatureReport.new
302
- feature_report.id = feature_id
303
- feature_report.name = feature_name
304
- feature_report.feature_type = feature_type
305
- feature_report.directory_name = workflow.absoluteRunDir
306
-
307
- timesteps_per_hour = model.getTimestep.numberOfTimestepsPerHour
308
- feature_report.timesteps_per_hour = timesteps_per_hour
309
-
310
- feature_report.simulation_status = 'Complete'
311
-
312
- feature_report.reporting_periods << URBANopt::Scenario::DefaultReports::ReportingPeriod.new
313
-
314
- ###########################################################################
315
- ##
316
- # Get Location information and store in the feature_report
317
- ##
318
-
319
- # latitude
320
- latitude = epw_file.latitude
321
- feature_report.location.latitude = latitude
322
-
323
- # longitude
324
- longitude = epw_file.longitude
325
- feature_report.location.longitude = longitude
326
-
327
- # surface_elevation
328
- elev = sql_query(runner, sql_file, 'InputVerificationandResultsSummary', "TableName='General' AND RowName='Elevation' AND ColumnName='Value'")
329
- feature_report.location.surface_elevation = elev
330
-
331
- ##########################################################################
332
- ##
333
- # Get program information and store in the feature_report
334
- ##
335
-
336
- # floor_area
337
- floor_area = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Total Building Area' AND ColumnName='Area'")
338
- feature_report.program.floor_area = convert_units(floor_area, 'm^2', 'ft^2')
339
-
340
- # conditioned_area
341
- conditioned_area = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Net Conditioned Building Area' AND ColumnName='Area'")
342
- feature_report.program.conditioned_area = convert_units(conditioned_area, 'm^2', 'ft^2')
343
-
344
- # unconditioned_area
345
- unconditioned_area = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Building Area' AND RowName='Unconditioned Building Area' AND ColumnName='Area'")
346
- feature_report.program.unconditioned_area = convert_units(unconditioned_area, 'm^2', 'ft^2')
347
-
348
- # footprint_area
349
- feature_report.program.footprint_area = convert_units(floor_area, 'm^2', 'ft^2')
350
-
351
- # maximum_number_of_stories
352
- number_of_stories = building.standardsNumberOfStories.get if building.standardsNumberOfStories.is_initialized
353
- number_of_stories ||= 1
354
- feature_report.program.maximum_number_of_stories = number_of_stories
355
-
356
- # maximum_roof_height
357
- floor_to_floor_height = building.nominalFloortoFloorHeight.to_f
358
- maximum_roof_height = number_of_stories * floor_to_floor_height
359
- feature_report.program.maximum_roof_height = maximum_roof_height
360
-
361
- # maximum_number_of_stories_above_ground
362
- number_of_stories_above_ground = building.standardsNumberOfAboveGroundStories.get if building.standardsNumberOfAboveGroundStories.is_initialized
363
- number_of_stories_above_ground ||= 1
364
- feature_report.program.maximum_number_of_stories_above_ground = number_of_stories_above_ground
365
-
366
- # number_of_residential_units
367
- number_of_living_units = building.standardsNumberOfLivingUnits.to_i.get if building.standardsNumberOfLivingUnits.is_initialized
368
- number_of_living_units ||= 1
369
- feature_report.program.number_of_residential_units = number_of_living_units
370
-
371
- ## building_types
372
-
373
- # get an array of the model spaces
374
- spaces = model.getSpaces
375
-
376
- # get array of model space types
377
- space_types = model.getSpaceTypes
378
-
379
- # create a hash for space_type_areas (spcace types as keys and their areas as values)
380
- space_type_areas = {}
381
- model.getSpaceTypes.each do |space_type|
382
- building_type = space_type.standardsBuildingType
383
- if building_type.empty?
384
- building_type = 'unknown'
385
- else
386
- building_type = building_type.get
387
- end
388
- space_type_areas[building_type] = 0 if space_type_areas[building_type].nil?
389
- space_type_areas[building_type] += convert_units(space_type.floorArea, 'm^2', 'ft^2')
390
- end
391
-
392
- # create a hash for space_type_occupancy (spcace types as keys and their occupancy as values)
393
- space_type_occupancy = {}
394
- spaces.each do |space|
395
- if space.spaceType.empty?
396
- raise 'space.spaceType is empty. Make sure spaces have a space type'
397
- else
398
- building_type = space.spaceType.get.standardsBuildingType
399
- end
400
- if building_type.empty?
401
- building_type = 'unknown'
402
- else
403
- building_type = building_type.get
404
- end
405
- space_type_occupancy[building_type] = 0 if space_type_occupancy[building_type].nil?
406
- space_type_occupancy[building_type] += space.numberOfPeople
407
- end
408
-
409
- # combine all in a building_types array
410
- building_types = []
411
- for i in 0..(space_type_areas.size - 1)
412
- building_types << { building_type: space_type_areas.keys[i], floor_area: space_type_areas.values[i], maximum_occupancy: space_type_occupancy.values[i] }
413
- end
414
- # add results to the feature report JSON
415
- feature_report.program.building_types = building_types
416
-
417
- ## window_area
418
- # north_window_area
419
- 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
420
- feature_report.program.window_area[:north_window_area] = convert_units(north_window_area, 'm^2', 'ft^2')
421
- # south_window_area
422
- 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
423
- feature_report.program.window_area[:south_window_area] = convert_units(south_window_area, 'm^2', 'ft^2')
424
- # east_window_area
425
- 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
426
- feature_report.program.window_area[:east_window_area] = convert_units(east_window_area, 'm^2', 'ft^2')
427
- # west_window_area
428
- 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
429
- feature_report.program.window_area[:west_window_area] = convert_units(west_window_area, 'm^2', 'ft^2')
430
- # total_window_area
431
- total_window_area = north_window_area + south_window_area + east_window_area + west_window_area
432
- feature_report.program.window_area[:total_window_area] = convert_units(total_window_area, 'm^2', 'ft^2')
433
-
434
- ## wall_area
435
- # north_wall_area
436
- 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
437
- feature_report.program.wall_area[:north_wall_area] = convert_units(north_wall_area, 'm^2', 'ft^2')
438
- # south_wall_area
439
- 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
440
- feature_report.program.wall_area[:south_wall_area] = convert_units(south_wall_area, 'm^2', 'ft^2')
441
- # east_wall_area
442
- 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
443
- feature_report.program.wall_area[:east_wall_area] = convert_units(east_wall_area, 'm^2', 'ft^2')
444
- # west_wall_area
445
- 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
446
- feature_report.program.wall_area[:west_wall_area] = convert_units(west_wall_area, 'm^2', 'ft^2')
447
- # total_wall_area
448
- total_wall_area = north_wall_area + south_wall_area + east_wall_area + west_wall_area
449
- feature_report.program.wall_area[:total_wall_area] = convert_units(total_wall_area, 'm^2', 'ft^2')
450
-
451
- # total_roof_area
452
- total_roof_area = 0.0
453
- surfaces.each do |surface|
454
- if (surface.outsideBoundaryCondition == 'Outdoors') && (surface.surfaceType == 'RoofCeiling')
455
- total_roof_area += surface.netArea
456
- end
457
- end
458
- feature_report.program.roof_area[:total_roof_area] = convert_units(total_roof_area, 'm^2', 'ft^2')
459
-
460
- # orientation
461
- # RK: a more robust method should be implemented to find orientation(finding main axis of the building using aspect ratio)
462
- building_rotation = model.getBuilding.northAxis
463
- feature_report.program.orientation = building_rotation
464
-
465
- # aspect_ratio
466
- 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)'")
467
- 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)'")
468
- aspect_ratio = north_wall_area / east_wall_area if north_wall_area != 0 && east_wall_area != 0
469
- aspect_ratio ||= nil
470
- feature_report.program.aspect_ratio = aspect_ratio
471
-
472
- # total_construction_cost
473
- 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'")
474
- feature_report.program.total_construction_cost = total_construction_cost
475
-
476
- ############################################################################
477
- ##
478
- # Get Reporting Periods information and store in the feature_report
479
- ##
480
-
481
- # start_date
482
- # month
483
- begin_month = model.getRunPeriod.getBeginMonth
484
- feature_report.reporting_periods[0].start_date.month = begin_month
485
- # day_of_month
486
- begin_day_of_month = model.getRunPeriod.getBeginDayOfMonth
487
- feature_report.reporting_periods[0].start_date.day_of_month = begin_day_of_month
488
- # year
489
- begin_year = model.getYearDescription.calendarYear
490
- feature_report.reporting_periods[0].start_date.year = begin_year
491
-
492
- # end_date
493
- # month
494
- end_month = model.getRunPeriod.getEndMonth
495
- feature_report.reporting_periods[0].end_date.month = end_month
496
- # day_of_month
497
- end_day_of_month = model.getRunPeriod.getEndDayOfMonth
498
- feature_report.reporting_periods[0].end_date.day_of_month = end_day_of_month
499
- # year
500
- end_year = model.getYearDescription.calendarYear
501
- feature_report.reporting_periods[0].end_date.year = end_year
502
-
503
- # total_site_energy
504
- total_site_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Total Site Energy' AND ColumnName='Total Energy'")
505
- feature_report.reporting_periods[0].total_site_energy = convert_units(total_site_energy, 'GJ', 'kBtu')
506
-
507
- # total_source_energy
508
- total_source_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Total Source Energy' AND ColumnName='Total Energy'")
509
- feature_report.reporting_periods[0].total_source_energy = convert_units(total_source_energy, 'GJ', 'kBtu')
510
-
511
- # net_site_energy
512
- net_site_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Net Site Energy' AND ColumnName='Total Energy'")
513
- feature_report.reporting_periods[0].net_site_energy = convert_units(net_site_energy, 'GJ', 'kBtu')
514
-
515
- # net_source_energy
516
- net_source_energy = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Site and Source Energy' AND RowName='Net Source Energy' AND ColumnName='Total Energy'")
517
- feature_report.reporting_periods[0].net_source_energy = convert_units(net_source_energy, 'GJ', 'kBtu')
518
-
519
- # electricity
520
- electricity = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Electricity'")
521
- feature_report.reporting_periods[0].electricity = convert_units(electricity, 'GJ', 'kBtu')
522
-
523
- # natural_gas
524
- natural_gas = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Natural Gas'")
525
- feature_report.reporting_periods[0].natural_gas = convert_units(natural_gas, 'GJ', 'kBtu')
526
-
527
- # additional_fuel
528
- additional_fuel = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Additional Fuel'")
529
- feature_report.reporting_periods[0].additional_fuel = convert_units(additional_fuel, 'GJ', 'kBtu')
530
-
531
- # district_cooling
532
- district_cooling = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='District Cooling'")
533
- feature_report.reporting_periods[0].district_cooling = convert_units(district_cooling, 'GJ', 'kBtu')
534
-
535
- # district_heating
536
- district_heating = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='District Heating'")
537
- feature_report.reporting_periods[0].district_heating = convert_units(district_heating, 'GJ', 'kBtu')
538
-
539
- # water
540
- water = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='Total End Uses' AND ColumnName='Water'")
541
- # feature_report.reporting_periods[0].water = convert_units(water, 'm3', 'ft3')
542
- feature_report.reporting_periods[0].water = water
543
-
544
- # electricity_produced
545
- electricity_produced = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Electric Loads Satisfied' AND RowName='Total On-Site and Utility Electric Sources' AND ColumnName='Electricity'")
546
- feature_report.reporting_periods[0].electricity_produced = convert_units(electricity_produced, 'GJ', 'kBtu')
547
-
548
- ## end_uses
549
-
550
- # get fuel type as listed in the sql file
551
- fuel_type = ['Electricity', 'Natural Gas', 'Additional Fuel', 'District Cooling', 'District Heating', 'Water']
552
-
553
- # get enduses as listed in the sql file
554
- enduses = ['Heating', 'Cooling', 'Interior Lighting', 'Exterior Lighting', 'Interior Equipment', 'Exterior Equipment', 'Fans', 'Pumps',
555
- 'Heat Rejection', 'Humidification', 'Heat Recovery', 'Water Systems', 'Refrigeration', 'Generators']
556
-
557
- # loop through fuel types and enduses to fill in sql_query method
558
- fuel_type.each do |ft|
559
- enduses.each do |eu|
560
- sql_r = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='End Uses' AND RowName='#{eu}' AND ColumnName='#{ft}'")
561
-
562
- # report each query in its corresponding feature report obeject
563
- if ft.include? ' '
564
- x = ft.tr(' ', '_').downcase
565
- m = feature_report.reporting_periods[0].end_uses.send(x)
566
- else
567
- m = feature_report.reporting_periods[0].end_uses.send(ft.downcase)
568
-
569
- end
570
-
571
- if eu.include? ' '
572
- y = eu.tr(' ', '_').downcase
573
- m.send("#{y}=", convert_units(sql_r, 'GJ', 'kBtu'))
574
- else
575
- m.send("#{eu.downcase}=", convert_units(sql_r, 'GJ', 'kBtu'))
576
- end
577
- end
578
- end
579
-
580
- ### energy_production
581
- ## electricity_produced
582
- # photovoltaic
583
- photovoltaic_power = sql_query(runner, sql_file, 'AnnualBuildingUtilityPerformanceSummary', "TableName='Electric Loads Satisfied' AND RowName='Photovoltaic Power' AND ColumnName='Electricity'")
584
- feature_report.reporting_periods[0].energy_production[:electricity_produced][:photovoltaic] = convert_units(photovoltaic_power, 'GJ', 'kBtu')
585
-
586
- ## comfort_result
587
- # time_setpoint_not_met_during_occupied_cooling
588
- 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'")
589
- feature_report.reporting_periods[0].comfort_result[:time_setpoint_not_met_during_occupied_cooling] = time_setpoint_not_met_during_occupied_cooling
590
-
591
- # time_setpoint_not_met_during_occupied_heating
592
- 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'")
593
- feature_report.reporting_periods[0].comfort_result[:time_setpoint_not_met_during_occupied_heating] = time_setpoint_not_met_during_occupied_heating
594
-
595
- # time_setpoint_not_met_during_occupied_hour
596
- time_setpoint_not_met_during_occupied_hours = time_setpoint_not_met_during_occupied_heating + time_setpoint_not_met_during_occupied_cooling
597
- feature_report.reporting_periods[0].comfort_result[:time_setpoint_not_met_during_occupied_hours] = time_setpoint_not_met_during_occupied_hours
598
-
599
- ######################################## Reporting TImeseries Results FOR CSV File ######################################
600
-
601
- # Get the weather file run period (as opposed to design day run period)
602
- ann_env_pd = nil
603
- sql_file.availableEnvPeriods.each do |env_pd|
604
- env_type = sql_file.environmentType(env_pd)
605
- if env_type.is_initialized
606
- if env_type.get == OpenStudio::EnvironmentType.new('WeatherRunPeriod')
607
- ann_env_pd = env_pd
608
- end
609
- end
610
- end
611
-
612
- if ann_env_pd == false
613
- runner.registerError("Can't find a weather runperiod, make sure you ran an annual simulation, not just the design days.")
614
- return false
615
- end
616
-
617
- # timeseries we want to report
618
- requested_timeseries_names = [
619
- 'Electricity:Facility',
620
- 'ElectricityProduced:Facility',
621
- 'Gas:Facility',
622
- 'Cooling:Electricity',
623
- 'Heating:Electricity',
624
- 'InteriorLights:Electricity',
625
- 'ExteriorLights:Electricity',
626
- 'InteriorEquipment:Electricity',
627
- 'Fans:Electricity',
628
- 'Pumps:Electricity',
629
- 'WaterSystems:Electricity',
630
- 'Heating:Gas',
631
- 'WaterSystems:Gas',
632
- 'InteriorEquipment:Gas',
633
- 'DistrictCooling:Facility',
634
- 'DistrictHeating:Facility',
635
- 'District Cooling Chilled Water Rate',
636
- 'District Cooling Mass Flow Rate',
637
- 'District Cooling Inlet Temperature',
638
- 'District Cooling Outlet Temperature',
639
- 'District Heating Hot Water Rate',
640
- 'District Heating Mass Flow Rate',
641
- 'District Heating Inlet Temperature',
642
- 'District Heating Outlet Temperature'
643
- ]
644
-
645
- # add thermal comfort timeseries
646
- comfortTimeseries = ['Zone Thermal Comfort Fanger Model PMV', 'Zone Thermal Comfort Fanger Model PPD']
647
- requested_timeseries_names += comfortTimeseries
648
-
649
- # add additional power timeseries (for calculating transformer apparent power to compare to rating ) in VA
650
- powerTimeseries = ['Net Electric Energy', 'Electricity:Facility Power', 'ElectricityProduced:Facility Power', 'Electricity:Facility Apparent Power', 'ElectricityProduced:Facility Apparent Power', 'Net Power', 'Net Apparent Power']
651
- requested_timeseries_names += powerTimeseries
652
-
653
- # register info all timeseries
654
- runner.registerInfo("All timeseries: #{requested_timeseries_names}")
655
-
656
- # timeseries variables to keep to calculate power
657
- tsToKeep = ['Electricity:Facility', 'ElectricityProduced:Facility']
658
- tsToKeepIndexes = {}
659
-
660
- ### powerFactor ###
661
- # use power_factor default: 0.9
662
- # TODO: Set powerFactor default based on building type
663
- powerFactor = 0.9
664
-
665
- ### power_conversion ###
666
- # divide values by total_seconds to convert J to W (W = J/sec)
667
- # divide values by total_hours to convert kWh to kW (kW = kWh/hrs)
668
- total_seconds = (60 / timesteps_per_hour.to_f) * 60 # make sure timesteps_per_hour is a float in the division
669
- total_hours = 1 / timesteps_per_hour.to_f # make sure timesteps_per_hour is a float in the division
670
- # set power_conversion
671
- power_conversion = total_hours # we set the power conversio to total_hours since we want to convert lWh to kW
672
- puts "Power Converion: to convert kWh to kW values will be divided by #{power_conversion}"
673
-
674
- # number of values in each timeseries
675
- n = nil
676
- # all numeric timeseries values, transpose of CSV file (e.g. values[key_cnt] is column, values[key_cnt][i] is column and row)
677
- values = []
678
- tmpArray = []
679
- # since schedule value will have a bunch of key_values, we need to keep track of these as additional timeseries
680
- key_cnt = 0
681
- # this is recording the name of these final timeseries to write in the header of the CSV
682
- final_timeseries_names = []
683
-
684
- # loop over requested timeseries
685
- # rubocop: disable Metrics/BlockLength
686
- requested_timeseries_names.each_index do |i|
687
- timeseries_name = requested_timeseries_names[i]
688
- runner.registerInfo("TIMESERIES: #{timeseries_name}")
689
-
690
- # get all the key values that this timeseries can be reported for (e.g. if PMV is requested for each zone)
691
- key_values = sql_file.availableKeyValues('RUN PERIOD 1', 'Zone Timestep', timeseries_name)
692
- runner.registerInfo("KEY VALUES: #{key_values}")
693
- if key_values.empty?
694
- key_values = ['']
695
- end
696
-
697
- # sort keys
698
- sorted_keys = key_values.sort
699
- requested_keys = requested_timeseries_names
700
- final_keys = []
701
- # make sure aggregated timeseries are listed in sorted order before all individual feature timeseries
702
- sorted_keys.each do |k|
703
- if requested_keys.include? k
704
- final_keys << k
705
- end
706
- end
707
- sorted_keys.each do |k|
708
- if !requested_keys.include? k
709
- final_keys << k
710
- end
711
- end
712
-
713
- # loop over final keys
714
- final_keys.each_with_index do |key_value, key_i|
715
- new_timeseries_name = ''
716
-
717
- runner.registerInfo("!! TIMESERIES NAME: #{timeseries_name} AND key_value: #{key_value}")
718
-
719
- # check if we have to come up with a new name for the timeseries in our CSV header
720
- if key_values.size == 1
721
- # use timeseries name when only 1 keyvalue
722
- new_timeseries_name = timeseries_name
723
- else
724
- # use key_value name
725
- # special case for Zone Thermal Comfort: use both timeseries_name and key_value
726
- if timeseries_name.include? 'Zone Thermal Comfort'
727
- new_timeseries_name = timeseries_name + ' ' + key_value
728
- else
729
- new_timeseries_name = key_value
730
- end
731
- end
732
- # final_timeseries_names << new_timeseries_name
733
-
734
- # get the actual timeseries
735
- ts = sql_file.timeSeries(ann_env_pd.to_s, reporting_frequency.to_s, timeseries_name, key_value)
736
-
737
- if n.nil?
738
- # first timeseries should always be set
739
- runner.registerInfo('First timeseries')
740
- values[key_cnt] = ts.get.values
741
- n = values[key_cnt].size
742
- elsif ts.is_initialized
743
- runner.registerInfo('Is Initialized')
744
- values[key_cnt] = ts.get.values
745
- else
746
- runner.registerInfo('Is NOT Initialized')
747
- values[key_cnt] = Array.new(n, 0)
748
- end
749
-
750
- # unit conversion
751
- old_unit = ts.get.units if ts.is_initialized
752
-
753
- if timeseries_name.include? 'Gas'
754
- new_unit = 'kBtu'
755
- else
756
- new_unit = case old_unit.to_s
757
- when 'J'
758
- 'kWh'
759
- when 'kBtu'
760
- 'kWh'
761
- when 'gal'
762
- 'm3'
763
- end
764
- end
765
- # loop through each value and apply unit conversion
766
- os_vec = values[key_cnt]
767
- if !timeseries_name.include? 'Zone Thermal Comfort'
768
- for i in 0..os_vec.length - 1
769
- unless new_unit == old_unit || !ts.is_initialized
770
- os_vec[i] = OpenStudio.convert(os_vec[i], old_unit, new_unit).get
771
- end
772
- end
773
- end
774
-
775
- # keep certain timeseries to calculate power
776
- if tsToKeep.include? timeseries_name
777
- tsToKeepIndexes[timeseries_name] = key_cnt
778
- end
779
-
780
- # special processing: power
781
- if powerTimeseries.include? timeseries_name
782
- # special case: net series (subtract generation from load)
783
- if timeseries_name.include? 'Net'
784
-
785
- newVals = Array.new(n, 0)
786
- # Apparent power calculation
787
-
788
- if timeseries_name.include?('Apparent')
789
- (0..n - 1).each do |j|
790
- newVals[j] = (values[tsToKeepIndexes['Electricity:Facility']][j].to_f - values[tsToKeepIndexes['ElectricityProduced:Facility']][j].to_f) / power_conversion / powerFactor
791
- j += 1
792
- end
793
- new_unit = 'kVA'
794
- elsif timeseries_name.include? 'Net Electric Energy'
795
- (0..n - 1).each do |j|
796
- newVals[j] = (values[tsToKeepIndexes['Electricity:Facility']][j].to_f - values[tsToKeepIndexes['ElectricityProduced:Facility']][j].to_f)
797
- j += 1
798
- end
799
- new_unit = 'kWh'
800
- else
801
- runner.registerInfo('Power calc')
802
- # Power calculation
803
- (0..n - 1).each do |j|
804
- newVals[j] = (values[tsToKeepIndexes['Electricity:Facility']][j].to_f - values[tsToKeepIndexes['ElectricityProduced:Facility']][j].to_f) / power_conversion
805
- j += 1
806
- end
807
- new_unit = 'kW'
808
- end
809
-
810
- values[key_cnt] = newVals
811
- else
812
- tsToKeepIndexes.each do |key, indexValue|
813
- if timeseries_name.include? key
814
- runner.registerInfo("timeseries_name: #{timeseries_name}, key: #{key}")
815
- # use this timeseries
816
- newVals = Array.new(n, 0)
817
- # Apparent power calculation
818
- if timeseries_name.include?('Apparent')
819
- (0..n - 1).each do |j|
820
- newVals[j] = values[indexValue][j].to_f / power_conversion / powerFactor
821
- j += 1
822
- end
823
- new_unit = 'kVA'
824
- else
825
- # Power calculation
826
- (0..n - 1).each do |j|
827
- newVals[j] = values[indexValue][j].to_f / power_conversion
828
- j += 1
829
- end
830
- new_unit = 'kW'
831
- end
832
- values[key_cnt] = newVals
833
- end
834
- end
835
- end
836
- end
837
-
838
- # append units to headers
839
- new_timeseries_name += "(#{new_unit})"
840
- final_timeseries_names << new_timeseries_name
841
-
842
- # TODO: DELETE PUTS
843
- # puts " *********timeseries_name = #{timeseries_name}******************"
844
- # if timeseries_name.include? 'Power'
845
- # puts "values = #{values[key_cnt]}"
846
- # puts "units = #{new_unit}"
847
- # end
848
-
849
- # comfort results usually have multiple timeseries (per zone), aggregate into a single series with consistent name and use worst value at each timestep
850
- if comfortTimeseries.include? timeseries_name
851
-
852
- # set up array if 1st key_value
853
- if key_i == 0
854
- runner.registerInfo("SETTING UP NEW ARRAY FOR: #{timeseries_name}")
855
- tmpArray = Array.new(n, 0)
856
- end
857
-
858
- # add to array (keep max value at each timestep)
859
- (0..(n - 1)).each do |ind|
860
- # process negative and positive values differently
861
- tVal = values[key_cnt][ind].to_f
862
- if tVal < 0
863
- tmpArray[ind] = [tVal, tmpArray[ind]].min
864
- else
865
- tmpArray[ind] = [tVal, tmpArray[ind]].max
866
- end
867
- end
868
-
869
- # aggregate and save when all keyvalues have been processed
870
- if key_i == final_keys.size - 1
871
-
872
- hrsOutOfBounds = 0
873
- if timeseries_name === 'Zone Thermal Comfort Fanger Model PMV'
874
- (0..(n - 1)).each do |ind|
875
- # -0.5 < x < 0.5 is within bounds
876
- if values[key_cnt][ind].to_f > 0.5 || values[key_cnt][ind].to_f < -0.5
877
- hrsOutOfBounds += 1
878
- end
879
- end
880
- hrsOutOfBounds = hrsOutOfBounds.to_f / timesteps_per_hour
881
- elsif timeseries_name === 'Zone Thermal Comfort Fanger Model PPD'
882
- (0..(n - 1)).each do |ind|
883
- # > 20 is outside bounds
884
- if values[key_cnt][ind].to_f > 20
885
- hrsOutOfBounds += 1
886
- end
887
- end
888
- hrsOutOfBounds = hrsOutOfBounds.to_f / timesteps_per_hour
889
- else
890
- # this one is already scaled by timestep, no need to divide total
891
- (0..(n - 1)).each do |ind|
892
- hrsOutOfBounds += values[key_cnt][ind].to_f if values[key_cnt][ind].to_f > 0
893
- end
894
- end
895
-
896
- # save variable to feature_reports hash
897
- runner.registerInfo("timeseries #{timeseries_name}: hours out of bounds: #{hrsOutOfBounds}")
898
- if timeseries_name === 'Zone Thermal Comfort Fanger Model PMV'
899
- feature_report.reporting_periods[0].comfort_result[:hours_out_of_comfort_bounds_PMV] = hrsOutOfBounds
900
- elsif timeseries_name == 'Zone Thermal Comfort Fanger Model PPD'
901
- feature_report.reporting_periods[0].comfort_result[:hours_out_of_comfort_bounds_PPD] = hrsOutOfBounds
902
- end
903
-
904
- end
905
-
906
- end
907
-
908
- # increment key_cnt in new_keys loop
909
- key_cnt += 1
910
- end
911
- end
912
-
913
- # Add datime column
914
- datetimes = []
915
- # check what timeseries is available
916
- available_ts = sql_file.availableTimeSeries
917
- # get the timeseries for any of available timeseries
918
- ts_d = sql_file.timeSeries(ann_env_pd.to_s, reporting_frequency.to_s, available_ts[0], '')
919
- timeseries_d = ts_d.get
920
- # get formated datetimes
921
- timeseries_d.dateTimes.each do |datetime|
922
- datetimes << format_datetime(datetime.to_s)
923
- end
924
- # insert datetimes to values
925
- values.insert(0, datetimes)
926
- # insert datetime header to names
927
- final_timeseries_names.insert(0, 'Datetime')
928
-
929
- # rubocop: enable Metrics/BlockLength
930
- runner.registerInfo("new final_timeseries_names size: #{final_timeseries_names.size}")
931
-
932
- # Save the 'default_feature_reports.csv' file
933
- File.open('default_feature_reports.csv', 'w') do |file|
934
- file.puts(final_timeseries_names.join(','))
935
- (0...n).each do |l|
936
- line = []
937
- values.each_index do |j|
938
- line << values[j][l]
939
- end
940
- file.puts(line.join(','))
941
- end
942
- end
943
-
944
- # closing the sql file
945
- sql_file.close
946
-
947
- ############################# Adding timeseries_csv info to json report and saving CSV ################################
948
- # add csv info to feature_report
949
- feature_report.timeseries_csv.path = File.join(Dir.pwd, 'default_feature_reports.csv')
950
- feature_report.timeseries_csv.first_report_datetime = '0'
951
- feature_report.timeseries_csv.column_names = final_timeseries_names
952
-
953
- ##### Save the 'default_feature_reports.json' file
954
-
955
- feature_report_hash = feature_report.to_hash
956
-
957
- File.open('default_feature_reports.json', 'w') do |f|
958
- f.puts JSON.pretty_generate(feature_report_hash)
959
- # make sure data is written to the disk one way or the other
960
- begin
961
- f.fsync
962
- rescue StandardError
963
- f.flush
964
- end
965
- end
966
-
967
- # reporting final condition
968
- runner.registerFinalCondition('Default Feature Reports generated successfully.')
969
-
970
- true
971
- # end the run method
972
- end
973
- # end the measure
974
- end
975
- # rubocop:enable Metrics/AbcSize
976
- # rubocop:enable Naming/MethodName
977
-
978
- # register the measure to be used by the application
979
- DefaultFeatureReports.new.registerWithApplication