urbanopt-scenario 0.2.0.pre2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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