urbanopt-scenario 0.2.0.pre1 → 0.4.0

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 +77 -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 +81 -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
@@ -10,17 +10,21 @@
10
10
  },
11
11
  "author": "NREL",
12
12
  "dependencies": {
13
- "highlight.js": "^9.15.6",
14
- "json-schema-ref-parser": "^6.1.0",
13
+ "highlight.js": "^10.2.0",
14
+ "json-schema-ref-parser": "^9.0.6",
15
15
  "json-schema-view-js": "git+https://git@github.com/bgschiller/json-schema-view-js.git",
16
- "vuepress": "^1.2.0",
17
- "webpack-dev-middleware": "^3.6.0"
16
+ "vuepress": "^1.5.4",
17
+ "webpack-dev-middleware": "^3.7.2"
18
18
  },
19
19
  "devDependencies": {
20
- "braces": ">=2.3.1",
21
- "gh-pages": "^2.0.1",
22
- "js-yaml": ">=3.13.1",
23
- "serialize-javascript": ">=2.1.1",
24
- "set-value": ">=2.0.1"
20
+ "braces": "^3.0.2",
21
+ "dot-prop": "^5.3.0",
22
+ "gh-pages": "^3.1.0",
23
+ "js-yaml": "^3.14.0",
24
+ "minimist": ">=1.2.3",
25
+ "node-forge": ">=0.10.0",
26
+ "serialize-javascript": "^5.0.1",
27
+ "set-value": "^3.0.2",
28
+ "yargs-parser": ">=18.1.1"
25
29
  }
26
30
  }
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -36,10 +36,12 @@ require 'urbanopt/scenario/scenario_base'
36
36
  require 'urbanopt/scenario/scenario_csv'
37
37
  require 'urbanopt/scenario/scenario_post_processor_base'
38
38
  require 'urbanopt/scenario/scenario_post_processor_default'
39
+ require 'urbanopt/scenario/scenario_post_processor_opendss'
39
40
  require 'urbanopt/scenario/scenario_runner_base'
40
41
  require 'urbanopt/scenario/scenario_runner_osw'
41
42
  require 'urbanopt/scenario/simulation_dir_base'
42
43
  require 'urbanopt/scenario/simulation_dir_osw'
43
44
  require 'urbanopt/scenario/simulation_mapper_base'
45
+ require 'urbanopt/scenario/scenario_visualization'
44
46
 
45
47
  require 'urbanopt/scenario/default_reports'
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -31,10 +31,5 @@
31
31
  ##
32
32
  # Retrieve all default_reports classes.
33
33
  ##
34
- require 'urbanopt/scenario/default_reports/construction_cost'
35
- require 'urbanopt/scenario/default_reports/feature_report'
36
- require 'urbanopt/scenario/default_reports/logger'
37
- require 'urbanopt/scenario/default_reports/program'
38
- require 'urbanopt/scenario/default_reports/reporting_period'
39
- require 'urbanopt/scenario/default_reports/scenario_report'
40
- require 'urbanopt/scenario/default_reports/timeseries_csv'
34
+
35
+ require 'urbanopt/reporting/default_reports'
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -52,7 +52,6 @@ module URBANopt
52
52
 
53
53
  def initialize(name, root_dir, run_dir, feature_file, mapper_files_dir, csv_file, num_header_rows)
54
54
  super(name, root_dir, run_dir, feature_file)
55
-
56
55
  @mapper_files_dir = mapper_files_dir
57
56
  @csv_file = csv_file
58
57
  @num_header_rows = num_header_rows
@@ -73,13 +72,27 @@ module URBANopt
73
72
 
74
73
  # Require all simulation mappers in mapper_files_dir
75
74
  def load_mapper_files
76
- Dir.glob(File.join(@mapper_files_dir, '/*.rb')).each do |f|
77
- begin
78
- require(f)
79
- rescue LoadError => e
80
- @@logger.error(e.message)
81
- raise
82
- end
75
+ dirs = Dir.glob(File.join(@mapper_files_dir, '/*.rb'))
76
+ # order is not guaranteed...attempt to add Baseline first, then High Efficiency
77
+ ordered_dirs = []
78
+ bindex = dirs.find_index { |i| i.include? 'Baseline.rb' }
79
+ if bindex
80
+ ordered_dirs << dirs[bindex]
81
+ dirs.delete_at(bindex)
82
+ end
83
+ hindex = dirs.find_index { |i| i.include? 'HighEfficiency.rb' }
84
+ if hindex
85
+ ordered_dirs << dirs[hindex] if hindex
86
+ dirs.delete_at(hindex)
87
+ end
88
+ # then the rest
89
+ ordered_dirs += dirs
90
+
91
+ ordered_dirs.each do |f|
92
+ require(f)
93
+ rescue LoadError => e
94
+ @@logger.error(e.message)
95
+ raise
83
96
  end
84
97
  end
85
98
 
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -59,6 +59,13 @@ module URBANopt
59
59
  @feature.feature_type
60
60
  end
61
61
 
62
+ ##
63
+ # Gets the type of a feature
64
+ ##
65
+ def feature_location
66
+ @feature.feature_location
67
+ end
68
+
62
69
  ##
63
70
  # Return the directory that this datapoint will run in.
64
71
  ##
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -29,12 +29,12 @@
29
29
  # *********************************************************************************
30
30
 
31
31
  require 'urbanopt/scenario/scenario_post_processor_base'
32
- require 'urbanopt/scenario/default_reports'
33
- require 'urbanopt/scenario/default_reports/logger'
32
+ require 'urbanopt/reporting/default_reports'
34
33
 
35
34
  require 'csv'
36
35
  require 'json'
37
36
  require 'fileutils'
37
+ require 'sqlite3'
38
38
 
39
39
  module URBANopt
40
40
  module Scenario
@@ -47,10 +47,11 @@ module URBANopt
47
47
  def initialize(scenario_base)
48
48
  super(scenario_base)
49
49
 
50
- initialization_hash = { directory_name: scenario_base.run_dir, name: scenario_base.name, id: scenario_base.name }
51
- @scenario_result = URBANopt::Scenario::DefaultReports::ScenarioReport.new(initialization_hash)
50
+ @initialization_hash = { directory_name: scenario_base.run_dir, name: scenario_base.name, id: scenario_base.name }
51
+ @scenario_result = URBANopt::Reporting::DefaultReports::ScenarioReport.new(@initialization_hash)
52
+ @default_save_name = 'default_scenario_report'
52
53
 
53
- @@logger ||= URBANopt::Scenario::DefaultReports.logger
54
+ @@logger ||= URBANopt::Reporting::DefaultReports.logger
54
55
  end
55
56
 
56
57
  ##
@@ -70,7 +71,7 @@ module URBANopt
70
71
  # [parameters:]
71
72
  # +simulation_dir+ - _SimulationDirOSW_ - An object on SimulationDirOSW class.
72
73
  def add_simulation_dir(simulation_dir)
73
- feature_reports = URBANopt::Scenario::DefaultReports::FeatureReport.from_simulation_dir(simulation_dir)
74
+ feature_reports = URBANopt::Reporting::DefaultReports::FeatureReport.from_simulation_dir(simulation_dir)
74
75
 
75
76
  feature_reports.each do |feature_report|
76
77
  if feature_report.to_hash[:simulation_status] == 'Complete'
@@ -83,12 +84,84 @@ module URBANopt
83
84
  return feature_reports
84
85
  end
85
86
 
87
+ # Create database file with scenario-level results
88
+ # Sum values for each timestep across all features. Save to new table in a new database
89
+ def create_scenario_db_file(file_name = @default_save_name)
90
+ new_db_file = File.join(@initialization_hash[:directory_name], "#{file_name}.db")
91
+ scenario_db = SQLite3::Database.open new_db_file
92
+ scenario_db.execute "CREATE TABLE IF NOT EXISTS ReportData(
93
+ TimeIndex INTEGER,
94
+ ReportDataDictionaryIndex INTEGER,
95
+ Value INTEGER
96
+ )"
97
+
98
+ values_arr = []
99
+ feature_list = Pathname.new(@initialization_hash[:directory_name]).children.select(&:directory?) # Folders in the run/scenario directory
100
+ feature_1_name = File.basename(feature_list[0]) # Get name of first feature, so we can read eplusout.sql from there
101
+ uo_output_sql_file = File.join(@initialization_hash[:directory_name], feature_1_name, 'eplusout.sql')
102
+ feature_list.each do |feature| # Loop through each feature in the scenario
103
+ feature_db = SQLite3::Database.open uo_output_sql_file
104
+ # Doing "db.results_as_hash = true" is prettier, but in this case significantly slower.
105
+
106
+ # RDDI == 10 is the timestep value for facility electricity
107
+ elec_query = feature_db.query "SELECT TimeIndex, Value
108
+ FROM ReportData
109
+ WHERE (TimeIndex % 2) != 0
110
+ AND ReportDataDictionaryIndex=10 order by TimeIndex"
111
+
112
+ elec_query.each do |row| # Add up all the values for electricity usage across all Features at this timestep
113
+ # row[0] == TimeIndex, row[1] == Value
114
+ arr_match = values_arr.find { |v| v[:time_index] == row[0] }
115
+ if arr_match.nil?
116
+ # add new row to value_arr
117
+ values_arr << { time_index: row[0], elec_val: Float(row[1]), gas_val: 0 }
118
+ else
119
+ # running sum
120
+ arr_match[:elec_val] += Float(row[1])
121
+ end
122
+ end # End elec_query
123
+ elec_query.close
124
+
125
+ # RDDI == 255 is the timestep value for facility gas
126
+ gas_query = feature_db.query "SELECT TimeIndex, Value
127
+ FROM ReportData
128
+ WHERE (TimeIndex % 2) != 0
129
+ AND ReportDataDictionaryIndex=255 order by TimeIndex"
130
+
131
+ gas_query.each do |row|
132
+ # row[0] == TimeIndex, row[1] == Value
133
+ arr_match = values_arr.find { |v| v[:time_index] == row[0] }
134
+ if arr_match.nil?
135
+ # add new row to value_arr
136
+ values_arr << { time_index: row[0], gas_val: Float(row[1]), elec_val: 0 }
137
+ else
138
+ # running sum
139
+ arr_match[:gas_val] += Float(row[1])
140
+ end
141
+ end # End gas_query
142
+ gas_query.close
143
+ feature_db.close
144
+ end # End feature_list loop
145
+
146
+ elec_sql = []
147
+ gas_sql = []
148
+ values_arr.each do |i|
149
+ elec_sql << "(#{i[:time_index]}, 10, #{i[:elec_val]})"
150
+ gas_sql << "(#{i[:time_index]}, 255, #{i[:gas_val]})"
151
+ end
152
+
153
+ # Put summed Values into the database
154
+ scenario_db.execute("INSERT INTO ReportData (TimeIndex, ReportDataDictionaryIndex, Value) VALUES #{elec_sql.join(', ')}")
155
+ scenario_db.execute("INSERT INTO ReportData (TimeIndex, ReportDataDictionaryIndex, Value) VALUES #{gas_sql.join(', ')}")
156
+ scenario_db.close
157
+ end
158
+
86
159
  ##
87
160
  # Save scenario result
88
161
  ##
89
162
  # [parameters:]
90
163
  # +file_name+ - _String_ - Assign a name to the saved scenario results file
91
- def save(file_name = 'default_scenario_report')
164
+ def save(file_name = @default_save_name)
92
165
  @scenario_result.save
93
166
 
94
167
  return @scenario_result
@@ -0,0 +1,275 @@
1
+ # *********************************************************************************
2
+ # URBANopt (tm), 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/scenario_post_processor_base'
32
+ require 'urbanopt/reporting/default_reports'
33
+
34
+ require 'csv'
35
+ require 'json'
36
+ require 'fileutils'
37
+ require 'pathname'
38
+
39
+ module URBANopt
40
+ module Scenario
41
+ class OpenDSSPostProcessor
42
+ ##
43
+ # OpenDSSPostProcessor post-processes OpenDSS results to selected OpenDSS results and integrate them in scenario and feature reports.
44
+ ##
45
+ # [parameters:]
46
+ # +scenario_report+ - _ScenarioBase_ - An object of Scenario_report class.
47
+ # +opendss_results_dir_name+ - _directory name of opendss results
48
+ def initialize(scenario_report, opendss_results_dir_name = 'opendss')
49
+ if !scenario_report.nil?
50
+ @scenario_report = scenario_report
51
+ @opendss_results_dir = File.join(@scenario_report.directory_name, opendss_results_dir_name)
52
+ else
53
+ raise 'scenario_report is not valid'
54
+ end
55
+
56
+ # hash of column_name to array of values, does not get serialized to hash
57
+ @mutex = Mutex.new
58
+
59
+ # initialize opendss data
60
+ @opendss_data = {}
61
+
62
+ # initialize feature_reports data
63
+ @feature_reports_data = {}
64
+
65
+ # initialize logger
66
+ @@logger ||= URBANopt::Reporting::DefaultReports.logger
67
+ end
68
+
69
+ # load opendss data
70
+ def load_opendss_data
71
+ # load building features data
72
+ @scenario_report.feature_reports.each do |feature_report|
73
+ # read results from opendss
74
+ opendss_csv = CSV.read(File.join(@opendss_results_dir, 'results', 'Features', feature_report.id + '.csv'))
75
+ # add results to data
76
+ @opendss_data[feature_report.id] = opendss_csv
77
+ end
78
+
79
+ ## load transformers data
80
+
81
+ # transformers results directory path
82
+ tf_results_path = File.join(@opendss_results_dir, 'results', 'Transformers')
83
+
84
+ # get transformer ids
85
+ transformer_ids = []
86
+ Dir.entries(tf_results_path.to_s).select do |f|
87
+ if !File.directory? f
88
+ fn = File.basename(f, '.csv')
89
+ transformer_ids << fn
90
+ end
91
+ end
92
+
93
+ # add transformer results to @opendss_data
94
+ transformer_ids.each do |id|
95
+ # read results from transformers
96
+ transformer_csv = CSV.read(File.join(tf_results_path, id + '.csv'))
97
+ # add results to data
98
+ @opendss_data[id] = transformer_csv
99
+ end
100
+ end
101
+
102
+ # load feature report data
103
+ def load_feature_report_data
104
+ @scenario_report.feature_reports.each do |feature_report|
105
+ # read feature results
106
+ feature_csv = CSV.read(File.join(feature_report.timeseries_csv.path))
107
+ # add results to data
108
+ @feature_reports_data[feature_report.id] = feature_csv
109
+ end
110
+ end
111
+
112
+ # load feature report data and opendss data
113
+ def load_data
114
+ # load selected opendss data
115
+ load_opendss_data
116
+ # load selected feature reports data
117
+ load_feature_report_data
118
+ end
119
+
120
+ # merge data
121
+ def merge_data(feature_report_data, opendss_data)
122
+ output = CSV.generate do |csv|
123
+ opendss_data.each_with_index do |row, i|
124
+ if row.include? 'Datetime'
125
+ row.map { |header| header.prepend('opendss_') }
126
+ end
127
+ csv << (feature_report_data[i] + row[1..-1])
128
+ end
129
+ end
130
+
131
+ return output
132
+ end
133
+
134
+ # add feature reports for transformers
135
+ def save_transformers_reports
136
+ @opendss_data.keys.each do |k|
137
+ if k.include? 'Transformer'
138
+
139
+ # create transformer directory
140
+ transformer_dir = File.join(@scenario_report.directory_name, k)
141
+ FileUtils.mkdir_p(File.join(transformer_dir, 'feature_reports'))
142
+
143
+ # write data to csv
144
+ # store under voltages and over voltages
145
+ under_voltage_hrs = 0
146
+ over_voltage_hrs = 0
147
+
148
+ transformer_csv = CSV.generate do |csv|
149
+ @opendss_data[k].each_with_index do |row, i|
150
+ csv << row
151
+
152
+ if !row[1].include? 'loading'
153
+ if row[1].to_f > 1.05
154
+ over_voltage_hrs += 1
155
+ end
156
+
157
+ if row[1].to_f < 0.95
158
+ under_voltage_hrs += 1
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ # save transformer CSV report
165
+ File.write(File.join(transformer_dir, 'feature_reports', 'default_feature_report_opendss' + '.csv'), transformer_csv)
166
+
167
+ # create transformer report
168
+ transformer_report = URBANopt::Reporting::DefaultReports::FeatureReport.new(id: k, name: k, directory_name: transformer_dir, feature_type: 'Transformer',
169
+ timesteps_per_hour: @scenario_report.timesteps_per_hour,
170
+ simulation_status: 'complete')
171
+
172
+ # assign results to transfomrer report
173
+ transformer_report.power_distribution.over_voltage_hours = over_voltage_hrs
174
+ transformer_report.power_distribution.under_voltage_hours = under_voltage_hrs
175
+
176
+ ## save transformer JSON file
177
+ # transformer_hash
178
+ transformer_hash = transformer_report.to_hash
179
+ # transformer_hash.delete_if { |k, v| v.nil? }
180
+
181
+ json_name_path = File.join(transformer_dir, 'feature_reports', 'default_feature_report_opendss' + '.json')
182
+
183
+ # save the json file
184
+ File.open(json_name_path, 'w') do |f|
185
+ f.puts JSON.pretty_generate(transformer_hash)
186
+ # make sure data is written to the disk one way or the other
187
+ begin
188
+ f.fsync
189
+ rescue StandardError
190
+ f.flush
191
+ end
192
+ end
193
+
194
+ # add transformers reports to scenario_report
195
+ @scenario_report.feature_reports << transformer_report
196
+
197
+ end
198
+ end
199
+ end
200
+
201
+ ##
202
+ # Save csv report method
203
+ ##
204
+ # [parameters:]
205
+ # +feature_report+ - _feature report object_ - An onject of the feature report
206
+ # +updated_feature_report_csv+ - _CSV_ - An updated feature report csv
207
+ # +file_name+ - _String_ - Assigned name to save the file with no extension
208
+ def save_csv(feature_report, updated_feature_report_csv, file_name = 'default_feature_report')
209
+ File.write(File.join(feature_report.directory_name, 'feature_reports', "#{file_name}.csv"), updated_feature_report_csv)
210
+ end
211
+
212
+ ##
213
+ # create opendss json report results
214
+ ##
215
+ # [parameters:]
216
+ # +feature_report+ - _feature report object_ - An onject of the feature report
217
+ def add_summary_results(feature_report)
218
+ under_voltage_hrs = 0
219
+ over_voltage_hrs = 0
220
+
221
+ id = feature_report.id
222
+ @opendss_data[id].each_with_index do |row, i|
223
+ if !row[1].include? 'voltage'
224
+
225
+ if row[1].to_f > 1.05
226
+ over_voltage_hrs += 1
227
+ end
228
+
229
+ if row[1].to_f < 0.95
230
+ under_voltage_hrs += 1
231
+ end
232
+
233
+ end
234
+ end
235
+
236
+ # assign results to feature report
237
+ feature_report.power_distribution.over_voltage_hours = over_voltage_hrs
238
+ feature_report.power_distribution.under_voltage_hours = under_voltage_hrs
239
+
240
+ return feature_report
241
+ end
242
+
243
+ ##
244
+ # run opendss post_processor
245
+ ##
246
+ def run
247
+ @scenario_report.feature_reports.each do |feature_report|
248
+ # load data
249
+ load_data
250
+
251
+ # puts " @opendss data = #{@opendss_data}"
252
+
253
+ # get summary results
254
+ add_summary_results(feature_report)
255
+
256
+ # merge csv data
257
+ id = feature_report.id
258
+ updated_feature_csv = merge_data(@feature_reports_data[id], @opendss_data[id])
259
+
260
+ # save fetaure reports
261
+ feature_report.save_feature_report('default_feature_report_opendss')
262
+
263
+ # resave updated csv report
264
+ save_csv(feature_report, updated_feature_csv, 'default_feature_report_opendss')
265
+ end
266
+
267
+ # add transformer reports
268
+ save_transformers_reports
269
+
270
+ # save the updated scenario reports
271
+ @scenario_report.save(file_name = 'scenario_report_opendss')
272
+ end
273
+ end
274
+ end
275
+ end