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