openstudio-standards 0.8.5 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/data/standards/test_performance_expected_dd_results.csv +90 -90
- data/lib/openstudio-standards/btap/activity.rb +858 -61
- data/lib/openstudio-standards/btap/attributes.rb +297 -103
- data/lib/openstudio-standards/btap/bridging.rb +1335 -593
- data/lib/openstudio-standards/btap/btap.rb +60 -181
- data/lib/openstudio-standards/btap/carbon/btap_carbon.rb +199 -142
- data/lib/openstudio-standards/btap/common/constructions.json +1275 -0
- data/lib/openstudio-standards/btap/common/costs.csv +1969 -0
- data/lib/openstudio-standards/btap/common/costs_local_factors.csv +2315 -0
- data/lib/openstudio-standards/btap/common/materials_glazing.csv +61 -0
- data/lib/openstudio-standards/btap/{common_resources → common}/materials_hvac.csv +1685 -1685
- data/lib/openstudio-standards/btap/{common_resources → common}/materials_lighting.csv +266 -266
- data/lib/openstudio-standards/btap/common/materials_opaque.csv +214 -0
- data/lib/openstudio-standards/btap/common/necb_building_types.csv +35 -0
- data/lib/openstudio-standards/btap/common/necb_space_types.csv +110 -0
- data/lib/openstudio-standards/btap/common/thermal_bridging.csv +73 -0
- data/lib/openstudio-standards/btap/costing/btap_costing.rb +346 -429
- data/lib/openstudio-standards/btap/costing/database.rb +104 -0
- data/lib/openstudio-standards/btap/costing/daylighting_sensor_control_costing.rb +356 -344
- data/lib/openstudio-standards/btap/costing/dcv_costing.rb +282 -296
- data/lib/openstudio-standards/btap/costing/envelope_costing.rb +179 -192
- data/lib/openstudio-standards/btap/costing/heating_cooling_costing.rb +2477 -2316
- data/lib/openstudio-standards/btap/costing/led_lighting_costing.rb +127 -142
- data/lib/openstudio-standards/btap/costing/lighting_costing.rb +216 -198
- data/lib/openstudio-standards/btap/costing/nv_costing.rb +522 -523
- data/lib/openstudio-standards/btap/costing/pv_ground_costing.rb +649 -649
- data/lib/openstudio-standards/btap/costing/shw_costing.rb +626 -625
- data/lib/openstudio-standards/btap/costing/thermal_bridging_costing.rb +72 -0
- data/lib/openstudio-standards/btap/costing/ventilation_costing.rb +2363 -2361
- data/lib/openstudio-standards/btap/deprecated.rb +1033 -0
- data/lib/openstudio-standards/btap/envelope.rb +313 -572
- data/lib/openstudio-standards/btap/fileio.rb +152 -1070
- data/lib/openstudio-standards/btap/geometry.rb +486 -766
- data/lib/openstudio-standards/btap/linear_regression.rb +130 -0
- data/lib/openstudio-standards/btap/paths.rb +46 -0
- data/lib/openstudio-standards/btap/qaqc/analysis.rb +163 -0
- data/lib/openstudio-standards/btap/qaqc/data.rb +2293 -0
- data/lib/openstudio-standards/btap/qaqc/datapoint.rb +641 -0
- data/lib/openstudio-standards/btap/schedules.rb +11 -611
- data/lib/openstudio-standards/btap/structure.rb +574 -189
- data/lib/openstudio-standards/constructions/materials/information.rb +1 -1
- data/lib/openstudio-standards/create_typical/create_typical.rb +1 -1
- data/lib/openstudio-standards/exterior_lighting/create.rb +135 -85
- data/lib/openstudio-standards/exterior_lighting/data/typical_exterior_lighting.json +20 -0
- data/lib/openstudio-standards/exterior_lighting/information.rb +23 -2
- data/lib/openstudio-standards/geometry/create.rb +7 -3
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Model.rb +3 -2
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb +3 -2
- data/lib/openstudio-standards/prototypes/common/data/thermostat_schedule_lookup.json +84 -84
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +4 -4
- data/lib/openstudio-standards/refrigeration/create_walkin.rb +45 -5
- data/lib/openstudio-standards/refrigeration/data/refrigerated_walkins.csv +75 -75
- data/lib/openstudio-standards/schedules/parametric.rb +6 -0
- data/lib/openstudio-standards/service_water_heating/create_typical.rb +10 -3
- data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +30 -14
- data/lib/openstudio-standards/service_water_heating/information.rb +4 -1
- data/lib/openstudio-standards/sql_file/unmet_hours.rb +2 -1
- data/lib/openstudio-standards/standards/Standards.CoilCoolingDXSingleSpeed.rb +3 -1
- data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +3 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.boilers.json +12 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_properties.json +18432 -3072
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.boilers.json +26 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_properties.json +8532 -1422
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.boilers.json +26 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_properties.json +8532 -1422
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.boilers.json +16 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.computer_room_acs.json +30 -30
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_properties.json +7560 -1260
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.PumpVariableSpeed.rb +1 -2
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.boilers.json +16 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_properties.json +7560 -1260
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +3 -3
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.PumpVariableSpeed.rb +1 -2
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.boilers.json +22 -0
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_properties.json +7236 -1206
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.furnaces.json +223 -19
- data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.schedules.json +37 -1
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.boilers.json +3 -0
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.construction_properties.json +6900 -1150
- data/lib/openstudio-standards/standards/cbes/data/cbes.schedules.json +1 -1
- data/lib/openstudio-standards/standards/deer/data/deer.schedules.json +1 -1
- data/lib/openstudio-standards/standards/necb/BTAP1980TO2010/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +6 -4
- data/lib/openstudio-standards/standards/necb/NECB2011/NECB2011_QAQC.json +2988 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +25 -3
- data/lib/openstudio-standards/standards/necb/NECB2011/building_envelope.rb +672 -534
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Hospital.osm +1791 -1792
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMultiTower.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPTownHouse.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeHotel.osm +5 -6
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LargeOffice.osm +8 -9
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +10 -4
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +14 -15
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/Outpatient.osm +7 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +14 -6
- data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +542 -130
- data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +30 -63
- data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +7 -2
- data/lib/openstudio-standards/standards/necb/NECB2015/data/surface_thermal_transmittance.json +90 -0
- data/lib/openstudio-standards/standards/necb/NECB2015/necb_2015.rb +8 -6
- data/lib/openstudio-standards/standards/necb/NECB2017/data/surface_thermal_transmittance.json +76 -40
- data/lib/openstudio-standards/standards/necb/NECB2017/necb_2017.rb +2 -1
- data/lib/openstudio-standards/standards/necb/NECB2020/building_envelope.rb +0 -117
- data/lib/openstudio-standards/standards/necb/NECB2020/data/surface_thermal_transmittance.json +76 -45
- data/lib/openstudio-standards/standards/necb/NECB2020/necb_2020.rb +218 -0
- data/lib/openstudio-standards/standards/necb/common/construction_defaults.osm +12 -7
- data/lib/openstudio-standards/standards/standard.rb +1 -1
- data/lib/openstudio-standards/version.rb +1 -1
- data/lib/openstudio-standards.rb +9 -4
- metadata +59 -66
- data/data/inventory/thermal_bridging.csv +0 -90
- data/data/standards/OpenStudio_Standards-ashrae_90_1-ALL-comstock(space_types).xlsx +0 -0
- data/data/standards/openstudio_standards_duplicates_log.csv +0 -7962
- data/lib/openstudio-standards/btap/NECB_building_types.csv +0 -35
- data/lib/openstudio-standards/btap/NECB_space_types.csv +0 -109
- data/lib/openstudio-standards/btap/WeatherData1.json +0 -2110
- data/lib/openstudio-standards/btap/WeatherData1.xlsx +0 -0
- data/lib/openstudio-standards/btap/btap_result.rb +0 -1144
- data/lib/openstudio-standards/btap/btap_test_helper.rb +0 -86
- data/lib/openstudio-standards/btap/common_paths.rb +0 -81
- data/lib/openstudio-standards/btap/common_resources/ConstructionProperties.csv +0 -52
- data/lib/openstudio-standards/btap/common_resources/Constructions.csv +0 -37
- data/lib/openstudio-standards/btap/common_resources/construction_sets.csv +0 -1270
- data/lib/openstudio-standards/btap/common_resources/constructions_glazing.csv +0 -121
- data/lib/openstudio-standards/btap/common_resources/constructions_opaque.csv +0 -2256
- data/lib/openstudio-standards/btap/common_resources/costs.csv +0 -1897
- data/lib/openstudio-standards/btap/common_resources/costs_local_factors.csv +0 -2315
- data/lib/openstudio-standards/btap/common_resources/materials_glazing.csv +0 -35
- data/lib/openstudio-standards/btap/common_resources/materials_opaque.csv +0 -164
- data/lib/openstudio-standards/btap/costing/btap_measure_helper.rb +0 -359
- data/lib/openstudio-standards/btap/costing/btap_workflow.rb +0 -117
- data/lib/openstudio-standards/btap/costing/copy_test_results_files_to_expected_results.rb +0 -11
- data/lib/openstudio-standards/btap/costing/cost_building_from_file.rb +0 -136
- data/lib/openstudio-standards/btap/costing/costing_database_wrapper.rb +0 -173
- data/lib/openstudio-standards/btap/costing/dummy.epw +0 -8768
- data/lib/openstudio-standards/btap/costing/dummy.osm +0 -5320
- data/lib/openstudio-standards/btap/costing/neb_end_use_prices.csv +0 -42
- data/lib/openstudio-standards/btap/costing/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/btap/costing/necb_reference_runs.csv +0 -28705
- data/lib/openstudio-standards/btap/costing/parallel_tests.rb +0 -92
- data/lib/openstudio-standards/btap/costing/test_list.txt +0 -15
- data/lib/openstudio-standards/btap/costing/test_run_all_test_locally.rb +0 -27
- data/lib/openstudio-standards/btap/costing/test_run_costing_tests.rb +0 -80
- data/lib/openstudio-standards/btap/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/btap/utilities/Add_template_field_to_json.rb +0 -22
- data/lib/openstudio-standards/btap/utilities/convert_surfaces_to_adiabatic_necb_8426.rb +0 -204
- data/lib/openstudio-standards/btap/utilities/generate_space_types.rb +0 -8
- data/lib/openstudio-standards/btap/utilities/necb_to_epw_map.rb +0 -50
- data/lib/openstudio-standards/btap/utilities/os_sim_extract.rb +0 -48
- data/lib/openstudio-standards/btap/utilities/rename_surfaces.rb +0 -24
- data/lib/openstudio-standards/btap/utilities/round_surf_coords.rb +0 -32
- data/lib/openstudio-standards/btap/utilities/sched_create.rb +0 -145
- data/lib/openstudio-standards/btap/utilities/set_mult_to_adiabatic.rb +0 -53
- data/lib/openstudio-standards/btap/utilities/space_type.rb +0 -78
- data/lib/openstudio-standards/btap/utilities/weatherData1_xlsx_to_json.rb +0 -56
- data/lib/openstudio-standards/btap/vintagizer.rb +0 -161
- data/lib/openstudio-standards/btap/visualization.rb +0 -72
- data/lib/openstudio-standards/standards/necb/NECB2011/data/necb_2011_spacetype_info.csv +0 -225
- data/lib/openstudio-standards/standards/necb/common/btap_analysis.rb +0 -79
- data/lib/openstudio-standards/standards/necb/common/btap_data.rb +0 -2656
- data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +0 -653
- data/lib/openstudio-standards/standards/necb/common/necb_reference_runs.csv +0 -28705
- data/lib/openstudio-standards/standards/necb/common/utility_pricing_2025-02-20.csv +0 -92
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_frame.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_glazing.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/carbon_opaque.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/hvac_vent_ahu.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/lighting.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/lighting_sets.csv +0 -0
- /data/lib/openstudio-standards/btap/{common_resources → common}/locations.csv +0 -0
- /data/lib/openstudio-standards/btap/{costing → common}/mech_sizing.json +0 -0
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
require 'openstudio'
|
|
2
|
+
require 'securerandom'
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module BTAP
|
|
6
|
+
class Datapoint
|
|
7
|
+
def initialize(input_folder: File.join(__dir__, 'input'),
|
|
8
|
+
output_folder: File.join(__dir__, 'output'),
|
|
9
|
+
weather_folder: File.join(__dir__, 'weather'))
|
|
10
|
+
|
|
11
|
+
@failed = false
|
|
12
|
+
|
|
13
|
+
puts("INPUT FOLDER: #{input_folder}\n" \
|
|
14
|
+
"OUTPUT FOLDER: #{output_folder}")
|
|
15
|
+
|
|
16
|
+
# Set local temporary cache folders. These are created new for each
|
|
17
|
+
# datapoint. Location of the datapoint input folder. Could be local, cloud,
|
|
18
|
+
# or VM host.
|
|
19
|
+
datapoint_folder = File.join(__dir__, 'datapoint/')
|
|
20
|
+
@dp_temp_folder = File.join(datapoint_folder, SecureRandom.uuid + '_' + Process.pid.to_s)
|
|
21
|
+
|
|
22
|
+
# Determine whether the analysis was run on AWS S3.
|
|
23
|
+
@s3 = input_folder.start_with?('s3:')
|
|
24
|
+
|
|
25
|
+
# Clean up stale temp folders whose PID isn't running.
|
|
26
|
+
Dir.glob("#{datapoint_folder}/*").each do |dir|
|
|
27
|
+
begin
|
|
28
|
+
Process.kill(0, dir.rpartition('_').last.to_i)
|
|
29
|
+
rescue Errno::ESRCH, Errno::EPERM
|
|
30
|
+
FileUtils.rm_rf(dir)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
FileUtils.mkdir_p(@dp_temp_folder)
|
|
34
|
+
|
|
35
|
+
# For S3 inputs, download to the temp folder so files are accessible
|
|
36
|
+
# locally. For local inputs, read directly from the input folder.
|
|
37
|
+
if @s3
|
|
38
|
+
match = input_folder.match(%r{s3://(.*?)/(.*)})
|
|
39
|
+
@s3_bucket = match[1]
|
|
40
|
+
@s3_input_folder_object = match[2]
|
|
41
|
+
@s3_output_folder_object = output_folder.match(%r{s3://(.*?)/(.*)})[2]
|
|
42
|
+
puts("s3_bucket: #{@s3_bucket}\n" \
|
|
43
|
+
"s3_input_folder_object: #{@s3_input_folder_object}\n" \
|
|
44
|
+
"s3_output_folder_object: #{@s3_output_folder_object}")
|
|
45
|
+
|
|
46
|
+
s3_copy(source_folder: input_folder, target_folder: @dp_temp_folder)
|
|
47
|
+
run_options_path = File.join(@dp_temp_folder, 'run_options.yml')
|
|
48
|
+
else
|
|
49
|
+
raise("Input folder does not exist: #{input_folder}") unless Dir.exist?(input_folder)
|
|
50
|
+
run_options_path = File.join(input_folder, 'run_options.yml')
|
|
51
|
+
end
|
|
52
|
+
raise("Could not read input from #{run_options_path}") unless File.file?(run_options_path)
|
|
53
|
+
|
|
54
|
+
@options = YAML.load_file(run_options_path)
|
|
55
|
+
|
|
56
|
+
# Location of datapoint output folder. Could be local, cloud, or VM host.
|
|
57
|
+
@dp_output_folder = File.join(output_folder, @options[:datapoint_id])
|
|
58
|
+
|
|
59
|
+
# Show versions in YAML file.
|
|
60
|
+
@options[:os_git_revision] = OpenstudioStandards.git_revision
|
|
61
|
+
|
|
62
|
+
# Get users' inputs for the parameters needed for the calculation af net present value
|
|
63
|
+
@npv_start_year = @options[:npv_start_year]
|
|
64
|
+
@npv_end_year = @options[:npv_end_year]
|
|
65
|
+
@npv_discount_rate = @options[:npv_discount_rate]
|
|
66
|
+
@npv_discount_rate_carbon = @options[:npv_discount_rate_carbon]
|
|
67
|
+
|
|
68
|
+
# Save configuration to temp folder.
|
|
69
|
+
File.open(File.join(@dp_temp_folder, 'run_options.yml'), 'w') { |file| file.write(@options.to_yaml) }
|
|
70
|
+
begin
|
|
71
|
+
@options[:template] = 'NECB2011' if @options[:algorithm_type] == 'osm_batch'
|
|
72
|
+
@standard = Standard.build(@options[:template])
|
|
73
|
+
|
|
74
|
+
# Set use the convert_arg_to_bool method from the standard class to set the @oerd_utility_pricing_flag
|
|
75
|
+
# Always set oerd_utility_pricing to false until it can be shared freely.
|
|
76
|
+
@oerd_utility_pricing = false
|
|
77
|
+
@utility_pricing_year = @standard.convert_arg_to_f(variable: @options[:utility_pricing_year], default: 2020)
|
|
78
|
+
|
|
79
|
+
# This allows you to select the skeleton model from our built-in starting
|
|
80
|
+
# points. You can add a custom file as it will search the libary first.
|
|
81
|
+
model = @standard.load_building_type_from_library(building_type: @options[:building_type])
|
|
82
|
+
if false == model
|
|
83
|
+
osm_model_path = File.absolute_path(File.join(@s3 ? @dp_temp_folder : input_folder, @options[:building_type] + '.osm'))
|
|
84
|
+
raise("File #{osm_model_path} not found") unless File.exist?(osm_model_path)
|
|
85
|
+
|
|
86
|
+
model = BTAP::FileIO.load_osm(osm_model_path)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# If analysis type is `osm_batch`, do nothing to the OSM files other than
|
|
90
|
+
# add temporal outputs and weather data. Otherwise, modify OSM input with
|
|
91
|
+
# options.
|
|
92
|
+
if @options[:algorithm_type] == 'osm_batch'
|
|
93
|
+
@standard.set_output_variables(model: model, output_variables: @options[:output_variables])
|
|
94
|
+
@standard.set_output_meters(model: model, output_meters: @options[:output_meters])
|
|
95
|
+
model.getYearDescription.setDayofWeekforStartDay('Sunday')
|
|
96
|
+
OpenstudioStandards::Weather.model_set_building_location(
|
|
97
|
+
model,
|
|
98
|
+
weather_file_path: OpenstudioStandards::Weather.get_standards_weather_file_path(@options[:epw_file]))
|
|
99
|
+
else
|
|
100
|
+
@standard.model_apply_standard(
|
|
101
|
+
model: model,
|
|
102
|
+
construction_opt: @options[:construction_opt],
|
|
103
|
+
epw_file: @options[:epw_file],
|
|
104
|
+
custom_weather_folder: weather_folder,
|
|
105
|
+
btap_weather: @options[:btap_weather],
|
|
106
|
+
sizing_run_dir: File.join(@dp_temp_folder, 'sizing_folder'),
|
|
107
|
+
hvac_system_primary: @options[:hvac_system_primary],
|
|
108
|
+
hvac_system_dwelling_units: @options[:hvac_system_dwelling_units],
|
|
109
|
+
hvac_system_washrooms: @options[:hvac_system_washrooms],
|
|
110
|
+
hvac_system_corridor: @options[:hvac_system_corridor],
|
|
111
|
+
hvac_system_storage: @options[:hvac_system_storage],
|
|
112
|
+
primary_heating_fuel: @options[:primary_heating_fuel],
|
|
113
|
+
necb_reference_hp: @options[:necb_reference_hp],
|
|
114
|
+
necb_reference_hp_supp_fuel: @options[:necb_reference_hp_supp_fuel],
|
|
115
|
+
|
|
116
|
+
# Four options: @options[: (1) 'NECB_Default', (2) 'No DCV',
|
|
117
|
+
# (3) 'Occupancy-based DCV' , (4) 'CO2-based DCV'
|
|
118
|
+
dcv_type: @options[:dcv_type],
|
|
119
|
+
|
|
120
|
+
# Two options: @options[: (1) 'NECB_Default', (2) 'LED'
|
|
121
|
+
lights_type: @options[:lights_type],
|
|
122
|
+
lights_scale: @options[:lights_scale],
|
|
123
|
+
daylighting_type: @options[:daylighting_type],
|
|
124
|
+
|
|
125
|
+
# Two options: @options[: (1) 'NECB_Default', (2) 'add_daylighting_controls'
|
|
126
|
+
ecm_system_name: @options[:ecm_system_name],
|
|
127
|
+
ecm_system_zones_map_option: @options[:ecm_system_zones_map_option],
|
|
128
|
+
|
|
129
|
+
# (1) 'NECB_Default' (2) 'one_sys_per_floor' (3) 'one_sys_per_bldg'
|
|
130
|
+
erv_package: @options[:erv_package],
|
|
131
|
+
boiler_eff: @options[:boiler_eff],
|
|
132
|
+
|
|
133
|
+
# TODO: Inconsistent naming
|
|
134
|
+
unitary_cop: @options[:adv_dx_units],
|
|
135
|
+
furnace_eff: @options[:furnace_eff],
|
|
136
|
+
shw_eff: @options[:shw_eff],
|
|
137
|
+
ext_wall_cond: @options[:ext_wall_cond],
|
|
138
|
+
ext_floor_cond: @options[:ext_floor_cond],
|
|
139
|
+
ext_roof_cond: @options[:ext_roof_cond],
|
|
140
|
+
ground_wall_cond: @options[:ground_wall_cond],
|
|
141
|
+
ground_floor_cond: @options[:ground_floor_cond],
|
|
142
|
+
ground_roof_cond: @options[:ground_roof_cond],
|
|
143
|
+
door_construction_cond: @options[:door_construction_cond],
|
|
144
|
+
fixed_window_cond: @options[:fixed_window_cond],
|
|
145
|
+
glass_door_cond: @options[:glass_door_cond],
|
|
146
|
+
overhead_door_cond: @options[:overhead_door_cond],
|
|
147
|
+
skylight_cond: @options[:skylight_cond],
|
|
148
|
+
glass_door_solar_trans: @options[:glass_door_solar_trans],
|
|
149
|
+
fixed_wind_solar_trans: @options[:fixed_wind_solar_trans],
|
|
150
|
+
skylight_solar_trans: @options[:skylight_solar_trans],
|
|
151
|
+
fdwr_set: @options[:fdwr_set],
|
|
152
|
+
srr_opt: @options[:srr_opt],
|
|
153
|
+
srr_set: @options[:srr_set],
|
|
154
|
+
rotation_degrees: @options[:rotation_degrees],
|
|
155
|
+
scale_x: @options[:scale_x],
|
|
156
|
+
scale_y: @options[:scale_y],
|
|
157
|
+
scale_z: @options[:scale_z],
|
|
158
|
+
footprint_aspect_ratio: @options[:footprint_aspect_ratio],
|
|
159
|
+
pv_ground_type: @options[:pv_ground_type],
|
|
160
|
+
pv_ground_total_area_pv_panels_m2: @options[:pv_ground_total_area_pv_panels_m2],
|
|
161
|
+
pv_ground_tilt_angle: @options[:pv_ground_tilt_angle],
|
|
162
|
+
pv_ground_azimuth_angle: @options[:pv_ground_azimuth_angle],
|
|
163
|
+
pv_ground_module_description: @options[:pv_ground_module_description],
|
|
164
|
+
nv_type: @options[:nv_type],
|
|
165
|
+
nv_opening_fraction: @options[:nv_opening_fraction],
|
|
166
|
+
nv_temp_out_min: @options[:nv_temp_out_min],
|
|
167
|
+
nv_delta_temp_in_out: @options[:nv_delta_temp_in_out],
|
|
168
|
+
occupancy_loads_scale: @options[:occupancy_loads_scale],
|
|
169
|
+
electrical_loads_scale: @options[:electrical_loads_scale],
|
|
170
|
+
oa_scale: @options[:oa_scale],
|
|
171
|
+
infiltration_scale: @options[:infiltration_scale],
|
|
172
|
+
chiller_type: @options[:chiller_type],
|
|
173
|
+
output_variables: @options[:output_variables],
|
|
174
|
+
output_meters: @options[:output_meters],
|
|
175
|
+
airloop_economizer_type: @options[:airloop_economizer_type],
|
|
176
|
+
shw_scale: @options[:shw_scale],
|
|
177
|
+
baseline_system_zones_map_option: @options[:baseline_system_zones_map_option],
|
|
178
|
+
tbd_interpolate: @options[:tbd_interpolate],
|
|
179
|
+
tbd_option: @options[:tbd_option],
|
|
180
|
+
necb_hdd: @options[:necb_hdd],
|
|
181
|
+
boiler_fuel: @options[:boiler_fuel],
|
|
182
|
+
boiler_cap_ratio: @options[:boiler_cap_ratio],
|
|
183
|
+
swh_fuel: @options[:swh_fuel],
|
|
184
|
+
airloop_fancoils_heating: @options[:airloop_fancoils_heating],
|
|
185
|
+
oerd_utility_pricing: @oerd_utility_pricing)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Save model to disk.
|
|
189
|
+
puts "saving model to #{File.join(@dp_temp_folder, 'output.osm')}"
|
|
190
|
+
BTAP::FileIO.save_osm(model, File.join(@dp_temp_folder, 'output.osm'))
|
|
191
|
+
|
|
192
|
+
# Run annual simulation of model.
|
|
193
|
+
if @options[:run_annual_simulation]
|
|
194
|
+
@run_dir = File.join(@dp_temp_folder, 'run_dir')
|
|
195
|
+
puts "running simulation in #{@run_dir}"
|
|
196
|
+
@standard.model_run_simulation_and_log_errors(model, @run_dir)
|
|
197
|
+
raise("No SQL file found.") if model.sqlFile.empty?
|
|
198
|
+
# Create qaqc file and save it.
|
|
199
|
+
@qaqc = BTAP::Datapoint.build_qaqc(model, @standard, @options[:datapoint_id], @options[:analysis_id])
|
|
200
|
+
|
|
201
|
+
# Load the SQL file into model.
|
|
202
|
+
sql_path = OpenStudio::Path.new(File.join(@run_dir, 'run/eplusout.sql'))
|
|
203
|
+
unless OpenStudio.exists(sql_path)
|
|
204
|
+
raise(
|
|
205
|
+
OpenStudio::Error,
|
|
206
|
+
"openstudio.model.Model", "Results for the sizing run couldn't be found here: #{sql_path}.")
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
sql = OpenStudio::SqlFile.new(sql_path)
|
|
210
|
+
|
|
211
|
+
# Check to make sure the sql file is readable,
|
|
212
|
+
# which won't be true if EnergyPlus crashed during simulation.
|
|
213
|
+
unless sql.connectionOpen
|
|
214
|
+
raise(OpenStudio::Error, 'openstudio.model.Model', "The run failed. Look at the eplusout.err file in" \
|
|
215
|
+
"#{File.dirname(sql_path.to_s)} to see the cause.")
|
|
216
|
+
return false
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Attach the SQL file from the run to the sizing model
|
|
220
|
+
model.setSqlFile(sql)
|
|
221
|
+
|
|
222
|
+
post_analysis = nil
|
|
223
|
+
if @options[:enable_costing] or @options[:enable_carbon]
|
|
224
|
+
post_analysis = BTAP::DatapointAnalysis.new(
|
|
225
|
+
model: model,
|
|
226
|
+
output_folder: @dp_temp_folder,
|
|
227
|
+
standard: @standard,
|
|
228
|
+
qaqc: @qaqc)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
@cost_result = nil
|
|
232
|
+
if @options[:enable_costing]
|
|
233
|
+
@cost_result = post_analysis.run_costing(
|
|
234
|
+
costs_csv: Paths.costs_path,
|
|
235
|
+
factors_csv: Paths.costs_local_factors_path)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
@carbon_result = nil
|
|
239
|
+
if @options[:enable_carbon]
|
|
240
|
+
@carbon_result = post_analysis.run_carbon
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
@qaqc[:options] = @options # Command-line options
|
|
244
|
+
@btap_data = BTAP::Data.new(model: model,
|
|
245
|
+
runner: nil,
|
|
246
|
+
cost_result: @cost_result,
|
|
247
|
+
carbon_result: @carbon_result,
|
|
248
|
+
qaqc: @qaqc,
|
|
249
|
+
npv_start_year: @npv_start_year,
|
|
250
|
+
npv_end_year: @npv_end_year,
|
|
251
|
+
npv_discount_rate: @npv_discount_rate,
|
|
252
|
+
npv_discount_rate_carbon: @npv_discount_rate_carbon,
|
|
253
|
+
oerd_utility_pricing: @oerd_utility_pricing,
|
|
254
|
+
utility_pricing_year: @utility_pricing_year).btap_data
|
|
255
|
+
|
|
256
|
+
File.open(File.join(@dp_temp_folder, 'btap_data.json'), 'w') do |file|
|
|
257
|
+
file.write(JSON.pretty_generate(@btap_data.sort.to_h, allow_nan: true))
|
|
258
|
+
puts "Wrote File btap_data.json in #{Dir.pwd} "
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
File.open(File.join(@dp_temp_folder, 'qaqc.json'), 'w') do |file|
|
|
262
|
+
file.write(JSON.pretty_generate(@qaqc, allow_nan: true))
|
|
263
|
+
puts "Wrote File qaqc.json in #{Dir.pwd} "
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
self.output_hourly_data(model,@dp_temp_folder, @options[:datapoint_id])
|
|
267
|
+
self.output_timestep_data(model,@dp_temp_folder, @options[:datapoint_id])
|
|
268
|
+
end
|
|
269
|
+
rescue StandardError => error
|
|
270
|
+
@error = error
|
|
271
|
+
puts "Error occured: #{@error}"
|
|
272
|
+
File.open(File.join(@dp_temp_folder, 'error.txt'), 'w') do |f|
|
|
273
|
+
f.write(@error.message + "\n" + @error.backtrace.join("\n"))
|
|
274
|
+
end
|
|
275
|
+
@failed = true
|
|
276
|
+
ensure
|
|
277
|
+
if @s3
|
|
278
|
+
@dp_output_folder = File.join(@s3_output_folder_object, @options[:datapoint_id])
|
|
279
|
+
s3_copy_folder_to_s3(bucket_name: @s3_bucket,
|
|
280
|
+
source_folder: @dp_temp_folder,
|
|
281
|
+
target_folder: @dp_output_folder)
|
|
282
|
+
else
|
|
283
|
+
|
|
284
|
+
# Copy results to datapoint output folder.
|
|
285
|
+
@dp_output_folder = File.join(output_folder, @options[:datapoint_id])
|
|
286
|
+
FileUtils.rm_rf(@dp_output_folder)
|
|
287
|
+
FileUtils.mkdir_p(@dp_output_folder)
|
|
288
|
+
FileUtils.cp_r(File.join(@dp_temp_folder, '.'), @dp_output_folder)
|
|
289
|
+
puts "Copied output to your designated output folder in the #{@options[:datapoint_id]} subfolder."
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Clean temp/cache folder up.
|
|
293
|
+
FileUtils.rm_rf(@dp_temp_folder)
|
|
294
|
+
|
|
295
|
+
# Do not fail container if running on AWS, handle error on AWS instead to
|
|
296
|
+
# avoid isuses with large analyses.
|
|
297
|
+
unless @failed == false || @s3
|
|
298
|
+
raise(@error)
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
class << self
|
|
304
|
+
|
|
305
|
+
# Initializes the qaqc data structure. Scoped inside of the class so
|
|
306
|
+
# that it can be used in the intialization of this class as well as in
|
|
307
|
+
# BTAP::Analysis.
|
|
308
|
+
def build_qaqc(model, standard, datapoint_id, analysis_id)
|
|
309
|
+
qaqc = standard.init_qaqc(model)
|
|
310
|
+
command = "SELECT Value
|
|
311
|
+
FROM TabularDataWithStrings
|
|
312
|
+
WHERE ReportName='LEEDsummary'
|
|
313
|
+
AND ReportForString='Entire Facility'
|
|
314
|
+
AND TableName='Sec1.1A-General Information'
|
|
315
|
+
AND RowName = 'Principal Heating Source'
|
|
316
|
+
AND ColumnName='Data'"
|
|
317
|
+
value = model.sqlFile.get.execAndReturnFirstString(command)
|
|
318
|
+
qaqc[:building][:principal_heating_source] = 'unknown'
|
|
319
|
+
unless value.empty?
|
|
320
|
+
qaqc[:building][:principal_heating_source] = value.get
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
if qaqc[:building][:principal_heating_source] == 'Additional Fuel'
|
|
324
|
+
model.getPlantLoops.sort.each do |iplantloop|
|
|
325
|
+
boilers = iplantloop.components.select { |icomponent| icomponent.to_BoilerHotWater.is_initialized }
|
|
326
|
+
qaqc[:building][:principal_heating_source] = 'FuelOilNo2' unless boilers.select { |boiler| boiler.to_BoilerHotWater.get.fuelType.to_s == 'FuelOilNo2' }.empty?
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
qaqc[:aws_datapoint_id] = datapoint_id
|
|
331
|
+
qaqc[:aws_analysis_id] = analysis_id
|
|
332
|
+
|
|
333
|
+
return qaqc
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def s3_copy_file_to_s3(bucket_name:, source_file:, target_file:, n: 0)
|
|
338
|
+
require 'aws-sdk-s3'
|
|
339
|
+
Aws.use_bundled_cert!
|
|
340
|
+
s3_client = Aws::S3::Client.new(region: 'ca-central-1')
|
|
341
|
+
transfer_manager = Aws::S3::TransferManager.new(client: s3_client)
|
|
342
|
+
|
|
343
|
+
puts("Copying File to S3. source_file:#{source_file} bucket:#{bucket_name} target_folder:#{target_file}")
|
|
344
|
+
response = nil
|
|
345
|
+
begin
|
|
346
|
+
result = transfer_manager.upload_file(source_file, bucket: bucket_name, key: target_file)
|
|
347
|
+
|
|
348
|
+
if result == true
|
|
349
|
+
puts "Object '#{source_file}' uploaded to bucket '#{bucket_name}'."
|
|
350
|
+
else
|
|
351
|
+
puts response
|
|
352
|
+
raise("Error:Object '#{source_file}' not uploaded to bucket '#{bucket_name}'.")
|
|
353
|
+
end
|
|
354
|
+
rescue StandardError => error
|
|
355
|
+
|
|
356
|
+
# Implementing exponential backoff timing out after 8 tries.
|
|
357
|
+
if n == 8
|
|
358
|
+
raise("Giving Up. Failed to submit send file #{source_file} #{target_file} in 8 tries while using exponential backoff. Error was:#{error}.")
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
wait_time = 2 ** n + rand
|
|
362
|
+
puts "Implementing exponential backoff for sending #{target_file} for #{wait_time} seconds. Attempt #{n}..."
|
|
363
|
+
sleep(wait_time)
|
|
364
|
+
return s3_copy_file_to_s3(bucket_name: bucket_name, source_file: source_file, target_file: target_file, n: n + 1)
|
|
365
|
+
end
|
|
366
|
+
return response
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def s3_copy_folder_to_s3(bucket_name:, source_folder:, target_folder:)
|
|
370
|
+
puts("Copying Folder to S3. source_folder:#{source_folder} bucket:#{bucket_name} target_folder:#{target_folder}")
|
|
371
|
+
|
|
372
|
+
# Iterate over files in folder.
|
|
373
|
+
Dir[File.join(source_folder, '**/*')].reject { |fn| File.directory?(fn) }.each do |source_file|
|
|
374
|
+
|
|
375
|
+
# Convert to S3 path. Removing source parent folders and replacing with s3 output folder.
|
|
376
|
+
target_file = File.join(target_folder, source_file.gsub(source_folder, ''))
|
|
377
|
+
s3_copy_file_to_s3(bucket_name: bucket_name, source_file: source_file, target_file: target_file)
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def s3_copy(source_folder:, target_folder:)
|
|
382
|
+
require 'open3'
|
|
383
|
+
exit_code = nil
|
|
384
|
+
error = nil
|
|
385
|
+
Open3.popen2e('aws', 's3', 'cp', source_folder, target_folder, '--recursive') do |stdin, stdout_stderr, wait_thread|
|
|
386
|
+
error = stdout_stderr.read
|
|
387
|
+
exit_code = wait_thread.value
|
|
388
|
+
end
|
|
389
|
+
if exit_code != 0
|
|
390
|
+
raise(error)
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
return exit_code
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def output_hourly_data(model, output_folder, datapoint_id)
|
|
397
|
+
osm_path = File.join(output_folder, "run_dir/in.osm")
|
|
398
|
+
sql_path = File.join(output_folder, "run_dir/run/eplusout.sql")
|
|
399
|
+
csv_output = File.join(output_folder, "hourly.csv")
|
|
400
|
+
|
|
401
|
+
hours_of_year = []
|
|
402
|
+
d = Time.new(2006, 1, 1, 1)
|
|
403
|
+
(0...8760).each do |increment|
|
|
404
|
+
hours_of_year << (d + (60 * 60) * increment).strftime('%Y-%m-%d %H:%M')
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
array_of_hashes = []
|
|
408
|
+
|
|
409
|
+
# Find hourly outputs available for this datapoint.
|
|
410
|
+
query = "
|
|
411
|
+
SELECT ReportDataDictionaryIndex
|
|
412
|
+
FROM ReportDataDictionary
|
|
413
|
+
WHERE ReportingFrequency == 'Hourly'
|
|
414
|
+
"
|
|
415
|
+
# Get hourly data for each output.
|
|
416
|
+
model.sqlFile.get.execAndReturnVectorOfInt(query).get.each do |rdd_index|
|
|
417
|
+
|
|
418
|
+
# Get Name
|
|
419
|
+
query = "
|
|
420
|
+
SELECT Name
|
|
421
|
+
FROM ReportDataDictionary
|
|
422
|
+
WHERE ReportDataDictionaryIndex == #{rdd_index}
|
|
423
|
+
"
|
|
424
|
+
name = model.sqlFile.get.execAndReturnFirstString(query).get
|
|
425
|
+
|
|
426
|
+
# Get KeyValue
|
|
427
|
+
query = "
|
|
428
|
+
SELECT KeyValue
|
|
429
|
+
FROM ReportDataDictionary
|
|
430
|
+
WHERE ReportDataDictionaryIndex == #{rdd_index}
|
|
431
|
+
"
|
|
432
|
+
|
|
433
|
+
# In some cases KeyValue has a value and sometimes it does not. In some cases KeyValue is null. If the command
|
|
434
|
+
# below is run and KeyValue is null then the command fails and returns an error. The fix below assumes that if
|
|
435
|
+
# the command below fails it is because KeyValue is null. In that case the "key_value" variable is set to a
|
|
436
|
+
# blank.
|
|
437
|
+
begin
|
|
438
|
+
key_value = model.sqlFile.get.execAndReturnFirstString(query).get
|
|
439
|
+
rescue StandardError => error
|
|
440
|
+
key_value = ""
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
# Get Units
|
|
444
|
+
query = "
|
|
445
|
+
SELECT Units
|
|
446
|
+
FROM ReportDataDictionary
|
|
447
|
+
WHERE ReportDataDictionaryIndex == #{rdd_index}
|
|
448
|
+
"
|
|
449
|
+
units = model.sqlFile.get.execAndReturnFirstString(query).get
|
|
450
|
+
|
|
451
|
+
# Get hourly data
|
|
452
|
+
query = "
|
|
453
|
+
Select Value
|
|
454
|
+
FROM ReportData
|
|
455
|
+
WHERE
|
|
456
|
+
ReportDataDictionaryIndex = #{rdd_index}
|
|
457
|
+
"
|
|
458
|
+
hourly_values = model.sqlFile.get.execAndReturnVectorOfDouble(query).get
|
|
459
|
+
|
|
460
|
+
hourly_hash = Hash[hours_of_year.zip(hourly_values)]
|
|
461
|
+
|
|
462
|
+
data_hash = { "datapoint_id": datapoint_id, "Name": name, "KeyValue": key_value, "Units": units }.merge(hourly_hash)
|
|
463
|
+
array_of_hashes << data_hash
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
CSV.open(csv_output, "wb") do |csv|
|
|
467
|
+
unless array_of_hashes.empty?
|
|
468
|
+
csv << array_of_hashes.first.keys # adds the attributes name on the first line
|
|
469
|
+
array_of_hashes.each do |hash|
|
|
470
|
+
csv << hash.values
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
end
|
|
475
|
+
#=====================================================================================================================
|
|
476
|
+
def output_timestep_data(model, output_folder,datapoint_id)
|
|
477
|
+
osm_path = File.join(output_folder, "run_dir/in.osm")
|
|
478
|
+
sql_path = File.join(output_folder, "run_dir/run/eplusout.sql")
|
|
479
|
+
csv_output = File.join(output_folder, "timestep.csv")
|
|
480
|
+
#===================================================================================================================
|
|
481
|
+
# Get number of timesteps from the model
|
|
482
|
+
number_of_timesteps_per_hour = model.getTimestep.numberOfTimestepsPerHour
|
|
483
|
+
#===================================================================================================================
|
|
484
|
+
# Calculate number of timesteps of the whole year
|
|
485
|
+
number_of_timesteps_of_year = 365 * 24 * number_of_timesteps_per_hour
|
|
486
|
+
|
|
487
|
+
timesteps_of_year = []
|
|
488
|
+
d = Time.new(2006, 1, 1, 0)
|
|
489
|
+
(0...number_of_timesteps_of_year).each do |increment|
|
|
490
|
+
timesteps_of_year << (d + (60 * 60 / number_of_timesteps_per_hour) * increment).strftime('%Y-%m-%d %H:%M')
|
|
491
|
+
end
|
|
492
|
+
timesteps_index = Array(0..number_of_timesteps_of_year-1)
|
|
493
|
+
#===================================================================================================================
|
|
494
|
+
# Create a hash with indices ('timesteps_index') as keys and timesteps as values ('timesteps_of_year')
|
|
495
|
+
# Note from Sara Gilani: 'I had to do this, as when I used 'timesteps_of_year' as keys to create all hashes below
|
|
496
|
+
# (e.g. timestep_hash_datapoint_id, timestep_hash_name, timestep_hash_key_value, ...),
|
|
497
|
+
# it did not create the right number of timesteps for the whole year'
|
|
498
|
+
timesteps_of_year_with_index = Hash[timesteps_index.zip(timesteps_of_year)]
|
|
499
|
+
#===================================================================================================================
|
|
500
|
+
array_of_hashes = []
|
|
501
|
+
array_of_hashes_transposed = []
|
|
502
|
+
#===================================================================================================================
|
|
503
|
+
# Find timestep outputs available for this datapoint.
|
|
504
|
+
query = "
|
|
505
|
+
SELECT ReportDataDictionaryIndex
|
|
506
|
+
FROM ReportDataDictionary
|
|
507
|
+
WHERE ReportingFrequency == 'Zone Timestep'
|
|
508
|
+
"
|
|
509
|
+
#===================================================================================================================
|
|
510
|
+
# Get timestep data for each output.
|
|
511
|
+
model.sqlFile.get.execAndReturnVectorOfInt(query).get.each do |rdd_index|
|
|
512
|
+
#=================================================================================================================
|
|
513
|
+
# Get Name
|
|
514
|
+
query = "
|
|
515
|
+
SELECT Name
|
|
516
|
+
FROM ReportDataDictionary
|
|
517
|
+
WHERE ReportDataDictionaryIndex == #{rdd_index}
|
|
518
|
+
"
|
|
519
|
+
name = model.sqlFile.get.execAndReturnFirstString(query).get
|
|
520
|
+
#=================================================================================================================
|
|
521
|
+
# Get KeyValue
|
|
522
|
+
query = "
|
|
523
|
+
SELECT KeyValue
|
|
524
|
+
FROM ReportDataDictionary
|
|
525
|
+
WHERE ReportDataDictionaryIndex == #{rdd_index}
|
|
526
|
+
"
|
|
527
|
+
#=================================================================================================================
|
|
528
|
+
# In some cases KeyValue has a value and sometimes it does not. In some cases KeyValue is null. If the command
|
|
529
|
+
# below is run and KeyValue is null then the command fails and returns an error. The fix below assumes that if
|
|
530
|
+
# the command below fails it is because KeyValue is null. In that case the "key_value" variable is set to a
|
|
531
|
+
# blank.
|
|
532
|
+
begin
|
|
533
|
+
key_value = model.sqlFile.get.execAndReturnFirstString(query).get
|
|
534
|
+
rescue StandardError => error
|
|
535
|
+
key_value = ""
|
|
536
|
+
end
|
|
537
|
+
#=================================================================================================================
|
|
538
|
+
# Get Units
|
|
539
|
+
query = "
|
|
540
|
+
SELECT Units
|
|
541
|
+
FROM ReportDataDictionary
|
|
542
|
+
WHERE ReportDataDictionaryIndex == #{rdd_index}
|
|
543
|
+
"
|
|
544
|
+
units = model.sqlFile.get.execAndReturnFirstString(query).get
|
|
545
|
+
#=================================================================================================================
|
|
546
|
+
# Get timestep data
|
|
547
|
+
query = "
|
|
548
|
+
Select VariableValue
|
|
549
|
+
FROM ReportVariableData
|
|
550
|
+
WHERE
|
|
551
|
+
ReportVariableDataDictionaryIndex = #{rdd_index}
|
|
552
|
+
"
|
|
553
|
+
timestep_values = model.sqlFile.get.execAndReturnVectorOfDouble(query).get
|
|
554
|
+
|
|
555
|
+
timestep_hash = Hash[timesteps_index.zip(timestep_values)]
|
|
556
|
+
|
|
557
|
+
data_hash = { "datapoint_id": datapoint_id, "Name": name, "KeyValue": key_value, "Units": units }.merge(timestep_hash)
|
|
558
|
+
|
|
559
|
+
array_of_hashes << data_hash
|
|
560
|
+
|
|
561
|
+
#=================================================================================================================
|
|
562
|
+
# Create a hash of data for having timesteps as rows instead of columns that is used for hourly outputs START
|
|
563
|
+
number_of_timesteps = timesteps_of_year.length()
|
|
564
|
+
|
|
565
|
+
array = [datapoint_id]
|
|
566
|
+
array_datapoint_id = array.zip(*[array]*number_of_timesteps).flatten
|
|
567
|
+
timestep_hash_datapoint_id = Hash[timesteps_index.zip(array_datapoint_id)]
|
|
568
|
+
|
|
569
|
+
array = [name]
|
|
570
|
+
array_name = array.zip(*[array]*number_of_timesteps).flatten
|
|
571
|
+
timestep_hash_name = Hash[timesteps_index.zip(array_name)]
|
|
572
|
+
|
|
573
|
+
array = [key_value]
|
|
574
|
+
array_key_value = array.zip(*[array]*number_of_timesteps).flatten
|
|
575
|
+
timestep_hash_key_value = Hash[timesteps_index.zip(array_key_value)]
|
|
576
|
+
|
|
577
|
+
array = [units]
|
|
578
|
+
array_units = array.zip(*[array]*number_of_timesteps).flatten
|
|
579
|
+
timestep_hash_units = Hash[timesteps_index.zip(array_units)]
|
|
580
|
+
|
|
581
|
+
timestep_hash_values = Hash[timesteps_index.zip(timestep_values)]
|
|
582
|
+
|
|
583
|
+
data_hash_transposed = Hash.new
|
|
584
|
+
timestep_hash_values.keys.each do |key|
|
|
585
|
+
data_hash_transposed[key] = [
|
|
586
|
+
timesteps_of_year_with_index[key],
|
|
587
|
+
timestep_hash_datapoint_id[key],
|
|
588
|
+
timestep_hash_name[key],
|
|
589
|
+
timestep_hash_key_value[key],
|
|
590
|
+
timestep_hash_units[key],
|
|
591
|
+
timestep_hash_values[key]
|
|
592
|
+
]
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
array_of_hashes_transposed << data_hash_transposed
|
|
596
|
+
# Create a hash of data for having timesteps as rows instead of columns that is used for hourly outputs END
|
|
597
|
+
#=================================================================================================================
|
|
598
|
+
|
|
599
|
+
end #model.sqlFile.get.execAndReturnVectorOfInt(query).get.each do |rdd_index|
|
|
600
|
+
#===================================================================================================================
|
|
601
|
+
# # This csv file has timesteps as the first row
|
|
602
|
+
# CSV.open(csv_output, "wb") do |csv|
|
|
603
|
+
# unless array_of_hashes.empty?
|
|
604
|
+
# csv << array_of_hashes.first.keys # adds the attributes name on the first line
|
|
605
|
+
# array_of_hashes.each do |hash|
|
|
606
|
+
# csv << hash.values
|
|
607
|
+
# end
|
|
608
|
+
# end
|
|
609
|
+
# end
|
|
610
|
+
#===================================================================================================================
|
|
611
|
+
# This csv file has timesteps as the first column
|
|
612
|
+
header = ['Index', 'Timestep', 'datapoint_id', 'Name', 'KeyValue', 'Units', 'Value']
|
|
613
|
+
CSV.open(csv_output, "wb") do |csv|
|
|
614
|
+
csv << header
|
|
615
|
+
array_of_hashes_transposed.each do |hash|
|
|
616
|
+
hash.keys().each do |index|
|
|
617
|
+
row_index = index
|
|
618
|
+
row_timestep = hash[index][0]
|
|
619
|
+
row_datapoint_id = hash[index][1]
|
|
620
|
+
row_name = hash[index][2]
|
|
621
|
+
row_key_value = hash[index][3]
|
|
622
|
+
row_units = hash[index][4]
|
|
623
|
+
row_value = hash[index][5]
|
|
624
|
+
|
|
625
|
+
row = CSV::Row.new(header,[])
|
|
626
|
+
row['Index'] = row_index
|
|
627
|
+
row['Timestep'] = row_timestep
|
|
628
|
+
row['datapoint_id'] = row_datapoint_id
|
|
629
|
+
row['Name'] = row_name
|
|
630
|
+
row['KeyValue'] = row_key_value
|
|
631
|
+
row['Units'] = row_units
|
|
632
|
+
row['Value'] = row_value
|
|
633
|
+
csv << row
|
|
634
|
+
end
|
|
635
|
+
end
|
|
636
|
+
end
|
|
637
|
+
#===================================================================================================================
|
|
638
|
+
end #output_timestep_data
|
|
639
|
+
#=====================================================================================================================
|
|
640
|
+
end
|
|
641
|
+
end
|